Merge "Allow separate configuration of display color fade" into main
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
index a1719c9..76ab303 100644
--- a/apct-tests/perftests/core/src/android/os/OWNERS
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -1 +1,4 @@
-per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
+
+# Bug component: 345036
+per-file VibratorPerfTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/cmds/appops/OWNERS b/cmds/appops/OWNERS
index 999ea0e..2fe78c5 100644
--- a/cmds/appops/OWNERS
+++ b/cmds/appops/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
 include /core/java/android/permission/OWNERS
diff --git a/core/api/current.txt b/core/api/current.txt
index ff011cf..eaefe84 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9694,6 +9694,7 @@
     method public int describeContents();
     method public void enforceCallingUid();
     method @Nullable public String getAttributionTag();
+    method public int getDeviceId();
     method @Nullable public android.content.AttributionSource getNext();
     method @Nullable public String getPackageName();
     method public int getPid();
@@ -9709,6 +9710,7 @@
     ctor public AttributionSource.Builder(@NonNull android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource build();
     method @NonNull public android.content.AttributionSource.Builder setAttributionTag(@Nullable String);
+    method @NonNull public android.content.AttributionSource.Builder setDeviceId(int);
     method @Deprecated @NonNull public android.content.AttributionSource.Builder setNext(@Nullable android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setNextAttributionSource(@NonNull android.content.AttributionSource);
     method @NonNull public android.content.AttributionSource.Builder setPackageName(@Nullable String);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index cf26e12..083c445 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -850,6 +850,7 @@
     ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
     ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
     ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], @Nullable android.content.AttributionSource);
+    ctor public AttributionSource(int, int, @Nullable String, @Nullable String, @NonNull android.os.IBinder, @Nullable String[], int, @Nullable android.content.AttributionSource);
     method public void enforceCallingPid();
   }
 
@@ -4235,6 +4236,7 @@
     field public final boolean isTrustedOverlay;
     field public final boolean isVisible;
     field @NonNull public final String name;
+    field @NonNull public final android.graphics.Matrix transform;
     field @NonNull public final android.os.IBinder windowToken;
   }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 853528f..12ffdb3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -65,6 +65,7 @@
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.TransactionExecutor;
 import android.app.servertransaction.TransactionExecutorHelper;
+import android.app.servertransaction.WindowTokenClientController;
 import android.bluetooth.BluetoothFrameworkInitializer;
 import android.companion.virtual.VirtualDeviceManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -6221,6 +6222,18 @@
                 false /* clearPending */);
     }
 
+    @Override
+    public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+            @NonNull Configuration configuration, int displayId) {
+        WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
+                configuration, displayId);
+    }
+
+    @Override
+    public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) {
+        WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken);
+    }
+
     /**
      * Sends windowing mode change callbacks to {@link Activity} if applicable.
      *
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 49fb794..f7a43f4 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -163,6 +163,13 @@
     public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
             Configuration overrideConfig, int displayId);
 
+    /** Deliver {@link android.window.WindowContext} configuration change. */
+    public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+            @NonNull Configuration configuration, int displayId);
+
+    /** Deliver {@link android.window.WindowContext} window removal event. */
+    public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
+
     /** Deliver result from another activity. */
     public abstract void handleSendResult(
             @NonNull ActivityClientRecord r, List<ResultInfo> results, String reason);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4eaebe0..1f95497 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -3465,7 +3465,7 @@
         AttributionSource attributionSource = new AttributionSource(Process.myUid(),
                 Process.myPid(), mOpPackageName, attributionTag,
                 (renouncedPermissions != null) ? renouncedPermissions.toArray(new String[0]) : null,
-                nextAttributionSource);
+                getDeviceId(), nextAttributionSource);
         // If we want to access protected data on behalf of another app we need to
         // tell the OS that we opt in to participate in the attribution chain.
         if (nextAttributionSource != null) {
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
new file mode 100644
index 0000000..3ac642f
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
@@ -0,0 +1,135 @@
+/*
+ * 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.servertransaction;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
+
+    @Nullable
+    private IBinder mClientToken;
+    @Nullable
+    private Configuration mConfiguration;
+    private int mDisplayId;
+
+    @Override
+    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+            @NonNull PendingTransactionActions pendingActions) {
+        client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
+    }
+
+    // ObjectPoolItem implementation
+
+    private WindowContextConfigurationChangeItem() {}
+
+    /** Obtains an instance initialized with provided params. */
+    public static WindowContextConfigurationChangeItem obtain(
+            @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+        WindowContextConfigurationChangeItem instance =
+                ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
+        if (instance == null) {
+            instance = new WindowContextConfigurationChangeItem();
+        }
+        instance.mClientToken = requireNonNull(clientToken);
+        instance.mConfiguration = requireNonNull(config);
+        instance.mDisplayId = displayId;
+
+        return instance;
+    }
+
+    @Override
+    public void recycle() {
+        mClientToken = null;
+        mConfiguration = null;
+        mDisplayId = INVALID_DISPLAY;
+        ObjectPool.recycle(this);
+    }
+
+    // Parcelable implementation
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mClientToken);
+        dest.writeTypedObject(mConfiguration, flags);
+        dest.writeInt(mDisplayId);
+    }
+
+    /** Reads from Parcel. */
+    private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
+        mClientToken = in.readStrongBinder();
+        mConfiguration = in.readTypedObject(Configuration.CREATOR);
+        mDisplayId = in.readInt();
+    }
+
+    public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
+            new Creator<>() {
+                public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
+                    return new WindowContextConfigurationChangeItem(in);
+                }
+
+                public WindowContextConfigurationChangeItem[] newArray(int size) {
+                    return new WindowContextConfigurationChangeItem[size];
+                }
+    };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
+        return Objects.equals(mClientToken, other.mClientToken)
+                && Objects.equals(mConfiguration, other.mConfiguration)
+                && mDisplayId == other.mDisplayId;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mClientToken);
+        result = 31 * result + Objects.hashCode(mConfiguration);
+        result = 31 * result + mDisplayId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
+                + ", config=" + mConfiguration
+                + ", displayId=" + mDisplayId
+                + "}";
+    }
+}
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
new file mode 100644
index 0000000..ed52a64
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -0,0 +1,112 @@
+/*
+ * 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.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} window removal message.
+ * @hide
+ */
+public class WindowContextWindowRemovalItem extends ClientTransactionItem {
+
+    @Nullable
+    private IBinder mClientToken;
+
+    @Override
+    public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+            @NonNull PendingTransactionActions pendingActions) {
+        client.handleWindowContextWindowRemoval(mClientToken);
+    }
+
+    // ObjectPoolItem implementation
+
+    private WindowContextWindowRemovalItem() {}
+
+    /** Obtains an instance initialized with provided params. */
+    public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) {
+        WindowContextWindowRemovalItem instance =
+                ObjectPool.obtain(WindowContextWindowRemovalItem.class);
+        if (instance == null) {
+            instance = new WindowContextWindowRemovalItem();
+        }
+        instance.mClientToken = requireNonNull(clientToken);
+
+        return instance;
+    }
+
+    @Override
+    public void recycle() {
+        mClientToken = null;
+        ObjectPool.recycle(this);
+    }
+
+    // Parcelable implementation
+
+    /** Writes to Parcel. */
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mClientToken);
+    }
+
+    /** Reads from Parcel. */
+    private WindowContextWindowRemovalItem(@NonNull Parcel in) {
+        mClientToken = in.readStrongBinder();
+    }
+
+    public static final @NonNull Creator<WindowContextWindowRemovalItem> CREATOR = new Creator<>() {
+        public WindowContextWindowRemovalItem createFromParcel(Parcel in) {
+            return new WindowContextWindowRemovalItem(in);
+        }
+
+        public WindowContextWindowRemovalItem[] newArray(int size) {
+            return new WindowContextWindowRemovalItem[size];
+        }
+    };
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowContextWindowRemovalItem other = (WindowContextWindowRemovalItem) o;
+        return Objects.equals(mClientToken, other.mClientToken);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + Objects.hashCode(mClientToken);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "WindowContextWindowRemovalItem{clientToken=" + mClientToken + "}";
+    }
+}
diff --git a/core/java/android/app/servertransaction/WindowTokenClientController.java b/core/java/android/app/servertransaction/WindowTokenClientController.java
index 28e2040..5d123a0 100644
--- a/core/java/android/app/servertransaction/WindowTokenClientController.java
+++ b/core/java/android/app/servertransaction/WindowTokenClientController.java
@@ -27,6 +27,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.window.WindowContext;
 import android.window.WindowTokenClient;
@@ -41,6 +42,7 @@
  */
 public class WindowTokenClientController {
 
+    private static final String TAG = WindowTokenClientController.class.getSimpleName();
     private static WindowTokenClientController sController;
 
     private final Object mLock = new Object();
@@ -61,7 +63,7 @@
 
     /** Overrides the {@link #getInstance()} for test only. */
     @VisibleForTesting
-    public static void overrideInstance(@NonNull WindowTokenClientController controller) {
+    public static void overrideForTesting(@NonNull WindowTokenClientController controller) {
         synchronized (WindowTokenClientController.class) {
             sController = controller;
         }
@@ -90,7 +92,7 @@
         if (configuration == null) {
             return false;
         }
-        onWindowContainerTokenAttached(client, displayId, configuration);
+        onWindowContextTokenAttached(client, displayId, configuration);
         return true;
     }
 
@@ -116,7 +118,7 @@
         if (configuration == null) {
             return false;
         }
-        onWindowContainerTokenAttached(client, displayId, configuration);
+        onWindowContextTokenAttached(client, displayId, configuration);
         return true;
     }
 
@@ -153,7 +155,7 @@
         }
     }
 
-    private void onWindowContainerTokenAttached(@NonNull WindowTokenClient client, int displayId,
+    private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
             @NonNull Configuration configuration) {
         synchronized (mLock) {
             mWindowTokenClientMap.put(client.asBinder(), client);
@@ -161,4 +163,33 @@
         client.onConfigurationChanged(configuration, displayId,
                 false /* shouldReportConfigChange */);
     }
+
+    /** Called when receives {@link WindowContextConfigurationChangeItem}. */
+    public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+            @NonNull Configuration configuration, int displayId) {
+        final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+        if (windowTokenClient != null) {
+            windowTokenClient.onConfigurationChanged(configuration, displayId);
+        }
+    }
+
+    /** Called when receives {@link WindowContextWindowRemovalItem}. */
+    public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) {
+        final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+        if (windowTokenClient != null) {
+            windowTokenClient.onWindowTokenRemoved();
+        }
+    }
+
+    @Nullable
+    private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) {
+        final WindowTokenClient windowTokenClient;
+        synchronized (mLock) {
+            windowTokenClient = mWindowTokenClientMap.get(clientToken);
+        }
+        if (windowTokenClient == null) {
+            Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken);
+        }
+        return windowTokenClient;
+    }
 }
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index eb672dc..b159321 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -48,9 +48,11 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
 
 /**
  * Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -785,7 +787,25 @@
             return;
         }
         try {
-            mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+            if (RemoteViews.isAdapterConversionEnabled()) {
+                List<CompletableFuture<Void>> updateFutures = new ArrayList<>();
+                for (int i = 0; i < appWidgetIds.length; i++) {
+                    final int widgetId = appWidgetIds[i];
+                    updateFutures.add(CompletableFuture.runAsync(() -> {
+                        try {
+                            RemoteViews views = mService.getAppWidgetViews(mPackageName, widgetId);
+                            if (views.replaceRemoteCollections(viewId)) {
+                                updateAppWidget(widgetId, views);
+                            }
+                        } catch (Exception e) {
+                            Log.e(TAG, "Error notifying changes in RemoteViews", e);
+                        }
+                    }));
+                }
+                CompletableFuture.allOf(updateFutures.toArray(CompletableFuture[]::new)).join();
+            } else {
+                mService.notifyAppWidgetViewDataChanged(mPackageName, appWidgetIds, viewId);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 3dddbc0..d0bb2b9 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -115,14 +115,14 @@
     public AttributionSource(int uid, @Nullable String packageName,
             @Nullable String attributionTag, @NonNull IBinder token) {
         this(uid, Process.INVALID_PID, packageName, attributionTag, token,
-                /*renouncedPermissions*/ null, /*next*/ null);
+                /*renouncedPermissions*/ null, Context.DEVICE_ID_DEFAULT, /*next*/ null);
     }
 
     /** @hide */
     public AttributionSource(int uid, int pid, @Nullable String packageName,
             @Nullable String attributionTag, @NonNull IBinder token) {
         this(uid, pid, packageName, attributionTag, token, /*renouncedPermissions*/ null,
-                /*next*/ null);
+                Context.DEVICE_ID_DEFAULT, /*next*/ null);
     }
 
     /** @hide */
@@ -132,21 +132,23 @@
             @Nullable AttributionSource next) {
         this(uid, Process.INVALID_PID, packageName, attributionTag, sDefaultToken,
                 (renouncedPermissions != null)
-                ? renouncedPermissions.toArray(new String[0]) : null, /*next*/ next);
+                ? renouncedPermissions.toArray(new String[0]) : null, Context.DEVICE_ID_DEFAULT,
+                /*next*/ next);
     }
 
     /** @hide */
     public AttributionSource(@NonNull AttributionSource current, @Nullable AttributionSource next) {
         this(current.getUid(), current.getPid(), current.getPackageName(),
                 current.getAttributionTag(), current.getToken(),
-                current.mAttributionSourceState.renouncedPermissions, next);
+                current.mAttributionSourceState.renouncedPermissions, current.getDeviceId(), next);
     }
 
     /** @hide */
     public AttributionSource(int uid, int pid, @Nullable String packageName,
-            @Nullable String attributionTag, @Nullable String[] renouncedPermissions,
+            @Nullable String attributionTag, @Nullable String[] renouncedPermissions, int deviceId,
             @Nullable AttributionSource next) {
-        this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, next);
+        this(uid, pid, packageName, attributionTag, sDefaultToken, renouncedPermissions, deviceId,
+                next);
     }
 
     /** @hide */
@@ -155,6 +157,16 @@
             @Nullable String attributionTag, @NonNull IBinder token,
             @Nullable String[] renouncedPermissions,
             @Nullable AttributionSource next) {
+        this(uid, pid, packageName, attributionTag, token, renouncedPermissions,
+                Context.DEVICE_ID_DEFAULT, next);
+    }
+
+    /** @hide */
+    @TestApi
+    public AttributionSource(int uid, int pid, @Nullable String packageName,
+            @Nullable String attributionTag, @NonNull IBinder token,
+            @Nullable String[] renouncedPermissions,
+            int deviceId, @Nullable AttributionSource next) {
         mAttributionSourceState = new AttributionSourceState();
         mAttributionSourceState.uid = uid;
         mAttributionSourceState.pid = pid;
@@ -162,6 +174,7 @@
         mAttributionSourceState.packageName = packageName;
         mAttributionSourceState.attributionTag = attributionTag;
         mAttributionSourceState.renouncedPermissions = renouncedPermissions;
+        mAttributionSourceState.deviceId = deviceId;
         mAttributionSourceState.next = (next != null) ? new AttributionSourceState[]
                 {next.mAttributionSourceState} : new AttributionSourceState[0];
     }
@@ -197,25 +210,31 @@
     /** @hide */
     public AttributionSource withNextAttributionSource(@Nullable AttributionSource next) {
         return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
-                getToken(), mAttributionSourceState.renouncedPermissions, next);
+                getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), next);
     }
 
     /** @hide */
     public AttributionSource withPackageName(@Nullable String packageName) {
         return new AttributionSource(getUid(), getPid(), packageName, getAttributionTag(),
-               getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+               getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
     }
 
     /** @hide */
     public AttributionSource withToken(@NonNull Binder token) {
         return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
-                token, mAttributionSourceState.renouncedPermissions, getNext());
+                token, mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
     }
 
     /** @hide */
     public AttributionSource withPid(int pid) {
         return new AttributionSource(getUid(), pid, getPackageName(), getAttributionTag(),
-                getToken(), mAttributionSourceState.renouncedPermissions, getNext());
+                getToken(), mAttributionSourceState.renouncedPermissions, getDeviceId(), getNext());
+    }
+
+    /** @hide */
+    public AttributionSource withDeviceId(int deviceId) {
+        return new AttributionSource(getUid(), getPid(), getPackageName(), getAttributionTag(),
+                getToken(), mAttributionSourceState.renouncedPermissions, deviceId, getNext());
     }
 
     /** @hide */
@@ -259,6 +278,7 @@
         try {
             return new AttributionSource.Builder(uid)
                 .setPid(Process.myPid())
+                .setDeviceId(Context.DEVICE_ID_DEFAULT)
                 .setPackageName(AppGlobals.getPackageManager().getPackagesForUid(uid)[0])
                 .build();
         } catch (Exception ignored) {
@@ -497,6 +517,13 @@
     }
 
     /**
+     * The device ID for which permissions are checked.
+     */
+    public int getDeviceId() {
+        return mAttributionSourceState.deviceId;
+    }
+
+    /**
      * Unique token for that source.
      *
      * @hide
@@ -662,6 +689,19 @@
         }
 
         /**
+         * Set the device ID for this attribution source, permission check would happen
+         * against this device ID.
+         *
+         * @return the builder
+         */
+        public @NonNull Builder setDeviceId(int deviceId) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x12;
+            mAttributionSourceState.deviceId = deviceId;
+            return this;
+        }
+
+        /**
          * The next app to receive the permission protected data.
          *
          * @deprecated Use {@link setNextAttributionSource} instead.
@@ -703,6 +743,9 @@
             if ((mBuilderFieldsSet & 0x10) == 0) {
                 mAttributionSourceState.renouncedPermissions = null;
             }
+            if ((mBuilderFieldsSet & 0x12) == 0) {
+                mAttributionSourceState.deviceId = Context.DEVICE_ID_DEFAULT;
+            }
             if ((mBuilderFieldsSet & 0x20) == 0) {
                 mAttributionSourceState.next = null;
             }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c10062c..fe108a5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6546,6 +6546,14 @@
     public static final String EXTRA_REASON = "android.intent.extra.REASON";
 
     /**
+     * Intent extra: Whether to show the wipe progress UI or to skip it.
+     *
+     * <p>Type: boolean
+     * @hide
+     */
+    public static final String EXTRA_SHOW_WIPE_PROGRESS = "android.intent.extra.SHOW_WIPE_PROGRESS";
+
+    /**
      * {@hide}
      * This extra will be send together with {@link #ACTION_FACTORY_RESET}
      */
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index b43639a..19539c2 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2415,6 +2415,8 @@
         public int requireUserAction = USER_ACTION_UNSPECIFIED;
         /** {@hide} */
         public boolean applicationEnabledSettingPersistent = false;
+        /** {@hide} */
+        public int developmentInstallFlags = 0;
 
         private final ArrayMap<String, Integer> mPermissionStates;
 
@@ -2464,6 +2466,7 @@
             requireUserAction = source.readInt();
             packageSource = source.readInt();
             applicationEnabledSettingPersistent = source.readBoolean();
+            developmentInstallFlags = source.readInt();
         }
 
         /** {@hide} */
@@ -2495,6 +2498,7 @@
             ret.requireUserAction = requireUserAction;
             ret.packageSource = packageSource;
             ret.applicationEnabledSettingPersistent = applicationEnabledSettingPersistent;
+            ret.developmentInstallFlags = developmentInstallFlags;
             return ret;
         }
 
@@ -3159,6 +3163,7 @@
             pw.printPair("rollbackDataPolicy", rollbackDataPolicy);
             pw.printPair("applicationEnabledSettingPersistent",
                     applicationEnabledSettingPersistent);
+            pw.printHexPair("developmentInstallFlags", developmentInstallFlags);
             pw.println();
         }
 
@@ -3200,6 +3205,7 @@
             dest.writeInt(requireUserAction);
             dest.writeInt(packageSource);
             dest.writeBoolean(applicationEnabledSettingPersistent);
+            dest.writeInt(developmentInstallFlags);
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bef023e..3791777 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1437,6 +1437,16 @@
     public @interface InstallFlags {}
 
     /**
+     * Install flags that can only be used in development workflows (e.g. {@code adb install}).
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "INSTALL_DEVELOPMENT_" }, value = {
+            INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DevelopmentInstallFlags {}
+
+    /**
      * Flag parameter for {@link #installPackage} to indicate that you want to
      * replace an already installed package, if one exists.
      *
@@ -1646,6 +1656,14 @@
      */
     public static final int INSTALL_FROM_MANAGED_USER_OR_PROFILE = 1 << 26;
 
+    /**
+     * Flag parameter for {@link #installPackage} to force a non-staged update of an APEX. This is
+     * a development-only feature and should not be used on end user devices.
+     *
+     * @hide
+     */
+    public static final int INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE = 1;
+
     /** @hide */
     @IntDef(flag = true, value = {
             DONT_KILL_APP,
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 022f3c4..4323bf8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1780,6 +1780,15 @@
          * @hide
          */
         String KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER = "use_normal_brightness_mode_controller";
+
+        /**
+         * Key for disabling screen wake locks while apps are in cached state.
+         * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+         * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+         * @hide
+         */
+        String KEY_DISABLE_SCREEN_WAKE_LOCKS_WHILE_CACHED =
+                "disable_screen_wake_locks_while_cached";
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 70b72c8..b99996ff 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -433,7 +433,7 @@
     @BinderThread
     @Override
     public void showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
-            int flags, ResultReceiver resultReceiver) {
+            @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_IME_WRAPPER);
         mCaller.executeOrSendMessage(mCaller.obtainMessageIOOO(DO_SHOW_SOFT_INPUT,
                 flags, showInputToken, resultReceiver, statsToken));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index e472a40..44fed67 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -606,6 +606,7 @@
     InputConnection mStartedInputConnection;
     EditorInfo mInputEditorInfo;
 
+    @InputMethod.ShowFlags
     int mShowInputFlags;
     boolean mShowInputRequested;
     boolean mLastShowInputRequested;
@@ -930,8 +931,9 @@
          */
         @MainThread
         @Override
-        public void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
-                IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
+        public void showSoftInputWithToken(@InputMethod.ShowFlags int flags,
+                ResultReceiver resultReceiver, IBinder showInputToken,
+                @Nullable ImeTracker.Token statsToken) {
             mSystemCallingShowSoftInput = true;
             mCurShowInputToken = showInputToken;
             mCurStatsToken = statsToken;
@@ -949,7 +951,7 @@
          */
         @MainThread
         @Override
-        public void showSoftInput(int flags, ResultReceiver resultReceiver) {
+        public void showSoftInput(@InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
             ImeTracker.forLogging().onProgress(
                     mCurStatsToken, ImeTracker.PHASE_IME_SHOW_SOFT_INPUT);
             if (DEBUG) Log.v(TAG, "showSoftInput()");
@@ -1325,7 +1327,8 @@
          * InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
          */
         @Deprecated
-        public void toggleSoftInput(int showFlags, int hideFlags) {
+        public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
+                @InputMethodManager.HideFlags int hideFlags) {
             InputMethodService.this.onToggleSoftInput(showFlags, hideFlags);
         }
 
@@ -2797,18 +2800,16 @@
      * {@link #onEvaluateInputViewShown()}, {@link #onEvaluateFullscreenMode()},
      * and the current configuration to decide whether the input view should
      * be shown at this point.
-     * 
-     * @param flags Provides additional information about the show request,
-     * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
+     *
      * @param configChange This is true if we are re-showing due to a
      * configuration change.
      * @return Returns true to indicate that the window should be shown.
      */
-    public boolean onShowInputRequested(int flags, boolean configChange) {
+    public boolean onShowInputRequested(@InputMethod.ShowFlags int flags, boolean configChange) {
         if (!onEvaluateInputViewShown()) {
             return false;
         }
-        if ((flags&InputMethod.SHOW_EXPLICIT) == 0) {
+        if ((flags & InputMethod.SHOW_EXPLICIT) == 0) {
             if (!configChange && onEvaluateFullscreenMode() && !isInputViewShown()) {
                 // Don't show if this is not explicitly requested by the user and
                 // the input method is fullscreen unless it is already shown. That
@@ -2834,14 +2835,14 @@
      * exposed to IME authors as an overridable public method without {@code @CallSuper}, we have
      * to have this method to ensure that those internal states are always updated no matter how
      * {@link #onShowInputRequested(int, boolean)} is overridden by the IME author.
-     * @param flags Provides additional information about the show request,
-     * as per {@link InputMethod#showSoftInput InputMethod.showSoftInput()}.
+     *
      * @param configChange This is true if we are re-showing due to a
      * configuration change.
      * @return Returns true to indicate that the window should be shown.
      * @see #onShowInputRequested(int, boolean)
      */
-    private boolean dispatchOnShowInputRequested(int flags, boolean configChange) {
+    private boolean dispatchOnShowInputRequested(@InputMethod.ShowFlags int flags,
+            boolean configChange) {
         final boolean result = onShowInputRequested(flags, configChange);
         mInlineSuggestionSessionController.notifyOnShowInputRequested(result);
         if (result) {
@@ -3274,16 +3275,13 @@
      *
      * The input method will continue running, but the user can no longer use it to generate input
      * by touching the screen.
-     *
-     * @see InputMethodManager#HIDE_IMPLICIT_ONLY
-     * @see InputMethodManager#HIDE_NOT_ALWAYS
-     * @param flags Provides additional operating flags.
      */
-    public void requestHideSelf(int flags) {
+    public void requestHideSelf(@InputMethodManager.HideFlags int flags) {
         requestHideSelf(flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_FROM_IME);
     }
 
-    private void requestHideSelf(int flags, @SoftInputShowHideReason int reason) {
+    private void requestHideSelf(@InputMethodManager.HideFlags int flags,
+            @SoftInputShowHideReason int reason) {
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestHideSelf", mDumper,
                 null /* icProto */);
         mPrivOps.hideMySoftInput(flags, reason);
@@ -3292,12 +3290,8 @@
     /**
      * Show the input method's soft input area, so the user sees the input method window and can
      * interact with it.
-     *
-     * @see InputMethodManager#SHOW_IMPLICIT
-     * @see InputMethodManager#SHOW_FORCED
-     * @param flags Provides additional operating flags.
      */
-    public final void requestShowSelf(int flags) {
+    public final void requestShowSelf(@InputMethodManager.ShowFlags int flags) {
         ImeTracing.getInstance().triggerServiceDump("InputMethodService#requestShowSelf", mDumper,
                 null /* icProto */);
         mPrivOps.showMySoftInput(flags);
@@ -3457,7 +3451,8 @@
     /**
      * Handle a request by the system to toggle the soft input area.
      */
-    private void onToggleSoftInput(int showFlags, int hideFlags) {
+    private void onToggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
+            @InputMethodManager.HideFlags int hideFlags) {
         if (DEBUG) Log.v(TAG, "toggleSoftInput()");
         if (isInputViewShown()) {
             requestHideSelf(
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index ceaf337..2584f04 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -119,7 +119,7 @@
      *   crashes (if a handler is sometimes created on a thread without a Looper active), or race
      *   conditions, where the thread a handler is associated with is not what the author
      *   anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
-     *   explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+     *   explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
      *   similar. If the implicit thread local behavior is required for compatibility, use
      *   {@code new Handler(Looper.myLooper())} to make it clear to readers.
      *
@@ -144,7 +144,7 @@
      *   crashes (if a handler is sometimes created on a thread without a Looper active), or race
      *   conditions, where the thread a handler is associated with is not what the author
      *   anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
-     *   explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+     *   explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
      *   similar. If the implicit thread local behavior is required for compatibility, use
      *   {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers.
      */
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index 5c4aa4a..ea5499f 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -1,6 +1,19 @@
 {
   "presubmit": [
     {
+      "file_patterns": [
+        "[^/]*(Vibrator|Vibration)[^/]*\\.java",
+        "vibrator/.*"
+      ],
+      "name": "FrameworksVibratorCoreTests",
+      "options": [
+        {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    },
+    {
       "file_patterns": ["Bugreport[^/]*\\.java"],
       "name": "BugreportManagerTestCases",
       "options": [
diff --git a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
index d41a428..e91e04e 100644
--- a/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
+++ b/core/java/android/os/vibrator/persistence/VibrationXmlParser.java
@@ -28,7 +28,6 @@
 import com.android.internal.vibrator.persistence.XmlConstants;
 import com.android.internal.vibrator.persistence.XmlParserException;
 import com.android.internal.vibrator.persistence.XmlReader;
-import com.android.internal.vibrator.persistence.XmlSerializedVibration;
 import com.android.modules.utils.TypedXmlPullParser;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -178,25 +177,62 @@
             // Ensure XML starts with expected root tag.
             XmlReader.readDocumentStartTag(parser, XmlConstants.TAG_VIBRATION);
 
-            int parserFlags = 0;
-            if ((flags & FLAG_ALLOW_HIDDEN_APIS) != 0) {
-                parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
-            }
-
             // Parse root tag as a vibration effect.
-            XmlSerializedVibration<VibrationEffect> serializedVibration =
-                    VibrationEffectXmlParser.parseTag(parser, parserFlags);
+            VibrationEffect effect = parseTag(parser, flags);
 
             // Ensure XML ends after root tag is consumed.
             XmlReader.readDocumentEndTag(parser);
 
-            return serializedVibration.deserialize();
-        } catch (XmlParserException e) {
+            return effect;
+        } catch (XmlParserException | VibrationXmlParserException e) {
             Slog.w(TAG, "Error parsing vibration XML", e);
             return null;
         }
     }
 
+    /**
+     * Parses XML content from given open {@link TypedXmlPullParser} into a {@link VibrationEffect}.
+     *
+     * <p>The provided parser should be pointing to a start of a valid vibration XML (i.e. to a
+     * start <vibration> tag). No other parser position, including start of document, is considered
+     * valid.
+     *
+     * <p>This method parses as long as it reads a valid vibration XML, and until an end vibration
+     * tag. After a successful parsing, the parser will point to the end vibration tag (i.e. to a
+     * </vibration> tag).
+     *
+     * @throws IOException error parsing from given {@link TypedXmlPullParser}.
+     * @throws VibrationXmlParserException if the XML tag cannot be parsed into a
+     *      {@link VibrationEffect}. The given {@code parser} might be pointing to a child XML tag
+     *      that caused the parser failure.
+     *
+     * @hide
+     */
+    @NonNull
+    public static VibrationEffect parseTag(@NonNull TypedXmlPullParser parser, @Flags int flags)
+            throws IOException, VibrationXmlParserException {
+        int parserFlags = 0;
+        if ((flags & VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS) != 0) {
+            parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
+        }
+        try {
+            return VibrationEffectXmlParser.parseTag(parser, parserFlags).deserialize();
+        } catch (XmlParserException e) {
+            throw new VibrationXmlParserException("Error parsing vibration effect.", e);
+        }
+    }
+
+    /**
+     * Represents an error while parsing a vibration XML input.
+     *
+     * @hide
+     */
+    public static final class VibrationXmlParserException extends Exception {
+        private VibrationXmlParserException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+
     private VibrationXmlParser() {
     }
 }
diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS
index 28a24203..0809de2 100644
--- a/core/java/android/print/OWNERS
+++ b/core/java/android/print/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 47273
 
-svetoslavganov@android.com
-svetoslavganov@google.com
+anothermark@google.com
+kumarashishg@google.com
diff --git a/core/java/android/print/pdf/OWNERS b/core/java/android/print/pdf/OWNERS
deleted file mode 100644
index 28a24203..0000000
--- a/core/java/android/print/pdf/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 47273
-
-svetoslavganov@android.com
-svetoslavganov@google.com
diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS
index 28a24203..0809de2 100644
--- a/core/java/android/printservice/OWNERS
+++ b/core/java/android/printservice/OWNERS
@@ -1,4 +1,4 @@
 # Bug component: 47273
 
-svetoslavganov@android.com
-svetoslavganov@google.com
+anothermark@google.com
+kumarashishg@google.com
diff --git a/core/java/android/printservice/recommendation/OWNERS b/core/java/android/printservice/recommendation/OWNERS
deleted file mode 100644
index 28a24203..0000000
--- a/core/java/android/printservice/recommendation/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 47273
-
-svetoslavganov@android.com
-svetoslavganov@google.com
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index f256210..447c3bc 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -27,6 +27,7 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Typeface;
 import android.icu.lang.UCharacter;
 import android.icu.text.CaseMap;
 import android.icu.text.Edits;
@@ -2482,12 +2483,28 @@
         if (ellipsizeDip == 0) {
             return gettingCleaned.toString();
         } else {
-            // Truncate
-            final TextPaint paint = new TextPaint();
-            paint.setTextSize(42);
+            final float assumedFontSizePx = 42;
+            if (Typeface.getSystemFontMap().isEmpty()) {
+                // In the system server, the font files may not be loaded, so unable to perform
+                // ellipsize, so use the estimated char count for the ellipsize.
 
-            return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
-                    TextUtils.TruncateAt.END);
+                // The median of glyph widths of the Roboto is 0.57em, so use it as a reference
+                // of the glyph width.
+                final float assumedCharWidthInEm = 0.57f;
+                final float assumedCharWidthInPx = assumedFontSizePx * assumedCharWidthInEm;
+
+                // Even if the argument name is `ellipsizeDip`, the unit of this argument is pixels.
+                final int charCount = (int) ((ellipsizeDip + 0.5f) / assumedCharWidthInPx);
+                return TextUtils.trimToSize(gettingCleaned.toString(), charCount)
+                        + getEllipsisString(TruncateAt.END);
+            } else {
+                // Truncate
+                final TextPaint paint = new TextPaint();
+                paint.setTextSize(assumedFontSizePx);
+
+                return TextUtils.ellipsize(gettingCleaned.toString(), paint, ellipsizeDip,
+                        TextUtils.TruncateAt.END);
+            }
         }
     }
 
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 99a4f6b..6aa8506 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -34,6 +34,7 @@
 import android.util.Log;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastPrintWriter;
 
 import java.io.FileDescriptor;
@@ -184,6 +185,15 @@
         }
     }
 
+    /** Overrides the {@link #getWindowManagerService()} for test only. */
+    @VisibleForTesting
+    public static void overrideWindowManagerServiceForTesting(
+            @NonNull IWindowManager windowManager) {
+        synchronized (WindowManagerGlobal.class) {
+            sWindowManagerService = windowManager;
+        }
+    }
+
     @UnsupportedAppUsage
     public static IWindowSession getWindowSession() {
         synchronized (WindowManagerGlobal.class) {
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8c..0699bc1 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -32,6 +32,7 @@
 import android.util.AttributeSet;
 import android.util.TimeUtils;
 import android.util.Xml;
+import android.view.Choreographer;
 import android.view.InflateException;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -153,7 +154,13 @@
      */
     public static long getExpectedPresentationTimeNanos() {
         AnimationState state = sAnimationState.get();
-        return state.mExpectedPresentationTimeNanos;
+        if (state.animationClockLocked) {
+            return state.mExpectedPresentationTimeNanos;
+        }
+        // When this methoed is called outside of a Choreographer callback,
+        // we obtain the value of expectedPresentTimeNanos from the Choreographer.
+        // This helps avoid returning a time that could potentially be earlier than current time.
+        return Choreographer.getInstance().getLatestExpectedPresentTimeNanos();
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index ce2c180..467daa0 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -295,8 +295,8 @@
 
     @AnyThread
     static boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
-            @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
-            @Nullable ResultReceiver resultReceiver,
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+            int lastClickToolType, @Nullable ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
         final IInputMethodManager service = getService();
         if (service == null) {
@@ -312,7 +312,7 @@
 
     @AnyThread
     static boolean hideSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
-            @Nullable ImeTracker.Token statsToken, int flags,
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
             @Nullable ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
         final IInputMethodManager service = getService();
         if (service == null) {
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 6340388..5b4efd8 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -17,6 +17,7 @@
 package android.view.inputmethod;
 
 import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -36,6 +37,8 @@
 import com.android.internal.inputmethod.InlineSuggestionsRequestInfo;
 import com.android.internal.inputmethod.InputMethodNavButtonFlags;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 
 /**
@@ -269,6 +272,14 @@
      */
     @MainThread
     public void revokeSession(InputMethodSession session);
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SHOW_" }, value = {
+            SHOW_EXPLICIT,
+            SHOW_FORCED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface ShowFlags {}
     
     /**
      * Flag for {@link #showSoftInput}: this show has been explicitly
@@ -292,8 +303,6 @@
     /**
      * Request that any soft input part of the input method be shown to the user.
      *
-     * @param flags Provides additional information about the show request.
-     * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
      * @param resultReceiver The client requesting the show may wish to
      * be told the impact of their request, which should be supplied here.
      * The result code should be
@@ -308,7 +317,7 @@
      * @hide
      */
     @MainThread
-    public default void showSoftInputWithToken(int flags, ResultReceiver resultReceiver,
+    public default void showSoftInputWithToken(@ShowFlags int flags, ResultReceiver resultReceiver,
             IBinder showInputToken, @Nullable ImeTracker.Token statsToken) {
         showSoftInput(flags, resultReceiver);
     }
@@ -316,8 +325,6 @@
     /**
      * Request that any soft input part of the input method be shown to the user.
      * 
-     * @param flags Provides additional information about the show request.
-     * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
      * @param resultReceiver The client requesting the show may wish to
      * be told the impact of their request, which should be supplied here.
      * The result code should be
@@ -327,11 +334,12 @@
      * {@link InputMethodManager#RESULT_HIDDEN InputMethodManager.RESULT_HIDDEN}.
      */
     @MainThread
-    public void showSoftInput(int flags, ResultReceiver resultReceiver);
+    public void showSoftInput(@ShowFlags int flags, ResultReceiver resultReceiver);
 
     /**
      * Request that any soft input part of the input method be hidden from the user.
-     * @param flags Provides additional information about the show request.
+     *
+     * @param flags Provides additional information about the hide request.
      * Currently always 0.
      * @param resultReceiver The client requesting the show may wish to
      * be told the impact of their request, which should be supplied here.
@@ -354,7 +362,8 @@
 
     /**
      * Request that any soft input part of the input method be hidden from the user.
-     * @param flags Provides additional information about the show request.
+     *
+     * @param flags Provides additional information about the hide request.
      * Currently always 0.
      * @param resultReceiver The client requesting the show may wish to
      * be told the impact of their request, which should be supplied here.
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 48bf973..e4cc6d5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -39,6 +39,7 @@
 import android.annotation.DisplayContext;
 import android.annotation.DrawableRes;
 import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
@@ -122,6 +123,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.reflect.Proxy;
 import java.util.Arrays;
 import java.util.Collections;
@@ -2034,6 +2037,14 @@
         }
     }
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "SHOW_" }, value = {
+            SHOW_IMPLICIT,
+            SHOW_FORCED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ShowFlags {}
+
     /**
      * Flag for {@link #showSoftInput} to indicate that this is an implicit
      * request to show the input window, not as the result of a direct request
@@ -2065,10 +2076,8 @@
      *             {@link View#isFocused view focus}, and its containing window has
      *             {@link View#hasWindowFocus window focus}. Otherwise the call fails and
      *             returns {@code false}.
-     * @param flags Provides additional operating flags.  Currently may be
-     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
      */
-    public boolean showSoftInput(View view, int flags) {
+    public boolean showSoftInput(View view, @ShowFlags int flags) {
         // Re-dispatch if there is a context mismatch.
         final InputMethodManager fallbackImm = getFallbackInputMethodManagerIfNecessary(view);
         if (fallbackImm != null) {
@@ -2131,21 +2140,20 @@
      *             {@link View#isFocused view focus}, and its containing window has
      *             {@link View#hasWindowFocus window focus}. Otherwise the call fails and
      *             returns {@code false}.
-     * @param flags Provides additional operating flags.  Currently may be
-     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
      * @param resultReceiver If non-null, this will be called by the IME when
      * it has processed your request to tell you what it has done.  The result
      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
      * {@link #RESULT_HIDDEN}.
      */
-    public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
+    public boolean showSoftInput(View view, @ShowFlags int flags, ResultReceiver resultReceiver) {
         return showSoftInput(view, null /* statsToken */, flags, resultReceiver,
                 SoftInputShowHideReason.SHOW_SOFT_INPUT);
     }
 
-    private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken, int flags,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+    private boolean showSoftInput(View view, @Nullable ImeTracker.Token statsToken,
+            @ShowFlags int flags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         if (statsToken == null) {
             statsToken = ImeTracker.forLogging().onRequestShow(null /* component */,
                     Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT, reason);
@@ -2199,7 +2207,7 @@
      */
     @Deprecated
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768499)
-    public void showSoftInputUnchecked(int flags, ResultReceiver resultReceiver) {
+    public void showSoftInputUnchecked(@ShowFlags int flags, ResultReceiver resultReceiver) {
         synchronized (mH) {
             final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestShow(
                     null /* component */, Process.myUid(), ImeTracker.ORIGIN_CLIENT_SHOW_SOFT_INPUT,
@@ -2230,6 +2238,14 @@
         }
     }
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "HIDE_" }, value = {
+            HIDE_IMPLICIT_ONLY,
+            HIDE_NOT_ALWAYS,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface HideFlags {}
+
     /**
      * Flag for {@link #hideSoftInputFromWindow} and {@link InputMethodService#requestHideSelf(int)}
      * to indicate that the soft input window should only be hidden if it was not explicitly shown
@@ -2251,10 +2267,8 @@
      *
      * @param windowToken The token of the window that is making the request,
      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
-     * @param flags Provides additional operating flags.  Currently may be
-     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
      */
-    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
+    public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
         return hideSoftInputFromWindow(windowToken, flags, null);
     }
 
@@ -2276,21 +2290,19 @@
      *
      * @param windowToken The token of the window that is making the request,
      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
-     * @param flags Provides additional operating flags.  Currently may be
-     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
      * @param resultReceiver If non-null, this will be called by the IME when
      * it has processed your request to tell you what it has done.  The result
      * code you receive may be either {@link #RESULT_UNCHANGED_SHOWN},
      * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
      * {@link #RESULT_HIDDEN}.
      */
-    public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
+    public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
             ResultReceiver resultReceiver) {
         return hideSoftInputFromWindow(windowToken, flags, resultReceiver,
                 SoftInputShowHideReason.HIDE_SOFT_INPUT);
     }
 
-    private boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
+    private boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags,
             ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
         final ImeTracker.Token statsToken = ImeTracker.forLogging().onRequestHide(
                 null /* component */, Process.myUid(),
@@ -2493,12 +2505,6 @@
      * If not the input window will be displayed.
      * @param windowToken The token of the window that is making the request,
      * as returned by {@link View#getWindowToken() View.getWindowToken()}.
-     * @param showFlags Provides additional operating flags.  May be
-     * 0 or have the {@link #SHOW_IMPLICIT},
-     * {@link #SHOW_FORCED} bit set.
-     * @param hideFlags Provides additional operating flags.  May be
-     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
-     * {@link #HIDE_NOT_ALWAYS} bit set.
      *
      * @deprecated Use {@link #showSoftInput(View, int)} or
      * {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead.
@@ -2507,7 +2513,8 @@
      * has an effect if the calling app is the current IME focus.
      */
     @Deprecated
-    public void toggleSoftInputFromWindow(IBinder windowToken, int showFlags, int hideFlags) {
+    public void toggleSoftInputFromWindow(IBinder windowToken, @ShowFlags int showFlags,
+            @HideFlags int hideFlags) {
         ImeTracing.getInstance().triggerClientDump(
                 "InputMethodManager#toggleSoftInputFromWindow", InputMethodManager.this,
                 null /* icProto */);
@@ -2525,12 +2532,6 @@
      *
      * If the input window is already displayed, it gets hidden.
      * If not the input window will be displayed.
-     * @param showFlags Provides additional operating flags.  May be
-     * 0 or have the {@link #SHOW_IMPLICIT},
-     * {@link #SHOW_FORCED} bit set.
-     * @param hideFlags Provides additional operating flags.  May be
-     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
-     * {@link #HIDE_NOT_ALWAYS} bit set.
      *
      * @deprecated Use {@link #showSoftInput(View, int)} or
      * {@link #hideSoftInputFromWindow(IBinder, int)} explicitly instead.
@@ -2539,7 +2540,7 @@
      * has an effect if the calling app is the current IME focus.
      */
     @Deprecated
-    public void toggleSoftInput(int showFlags, int hideFlags) {
+    public void toggleSoftInput(@ShowFlags int showFlags, @HideFlags int hideFlags) {
         ImeTracing.getInstance().triggerClientDump(
                 "InputMethodManager#toggleSoftInput", InputMethodManager.this,
                 null /* icProto */);
@@ -3552,15 +3553,12 @@
      * @param token Supplies the identifying token given to an input method
      * when it was started, which allows it to perform this operation on
      * itself.
-     * @param flags Provides additional operating flags.  Currently may be
-     * 0 or have the {@link #HIDE_IMPLICIT_ONLY},
-     * {@link #HIDE_NOT_ALWAYS} bit set.
      * @deprecated Use {@link InputMethodService#requestHideSelf(int)} instead. This method was
      * intended for IME developers who should be accessing APIs through the service. APIs in this
      * class are intended for app developers interacting with the IME.
      */
     @Deprecated
-    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
+    public void hideSoftInputFromInputMethod(IBinder token, @HideFlags int flags) {
         InputMethodPrivilegedOperationsRegistry.get(token).hideMySoftInput(
                 flags, SoftInputShowHideReason.HIDE_SOFT_INPUT_IMM_DEPRECATION);
     }
@@ -3574,15 +3572,12 @@
      * @param token Supplies the identifying token given to an input method
      * when it was started, which allows it to perform this operation on
      * itself.
-     * @param flags Provides additional operating flags.  Currently may be
-     * 0 or have the {@link #SHOW_IMPLICIT} or
-     * {@link #SHOW_FORCED} bit set.
      * @deprecated Use {@link InputMethodService#requestShowSelf(int)} instead. This method was
      * intended for IME developers who should be accessing APIs through the service. APIs in this
      * class are intended for app developers interacting with the IME.
      */
     @Deprecated
-    public void showSoftInputFromInputMethod(IBinder token, int flags) {
+    public void showSoftInputFromInputMethod(IBinder token, @ShowFlags int flags) {
         InputMethodPrivilegedOperationsRegistry.get(token).showMySoftInput(flags);
     }
 
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index af6af14..4f48cb6 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -169,12 +169,6 @@
     /**
      * Toggle the soft input window.
      * Applications can toggle the state of the soft input window.
-     * @param showFlags Provides additional operating flags.  May be
-     * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT},
-     * {@link InputMethodManager#SHOW_FORCED} bit set.
-     * @param hideFlags Provides additional operating flags.  May be
-     * 0 or have the {@link  InputMethodManager#HIDE_IMPLICIT_ONLY},
-     * {@link  InputMethodManager#HIDE_NOT_ALWAYS} bit set.
      *
      * @deprecated Starting in {@link android.os.Build.VERSION_CODES#S} the system no longer invokes
      * this method, instead it explicitly shows or hides the IME. An {@code InputMethodService}
@@ -182,7 +176,8 @@
      * InputMethodService#requestShowSelf} or {@link InputMethodService#requestHideSelf}
      */
     @Deprecated
-    public void toggleSoftInput(int showFlags, int hideFlags);
+    public void toggleSoftInput(@InputMethodManager.ShowFlags int showFlags,
+            @InputMethodManager.HideFlags int hideFlags);
 
     /**
      * This method is called when the cursor and/or the character position relevant to text input
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index e9d7b9b..a95748c 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -43,7 +43,6 @@
 import android.view.View;
 import android.view.ViewRootImpl;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.infra.AndroidFuture;
 import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
 import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -54,6 +53,7 @@
 import java.lang.ref.WeakReference;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
 import java.util.function.Supplier;
 
@@ -158,18 +158,13 @@
         boolean cancellable();
     }
 
-    @GuardedBy("mLock")
-    @Nullable
-    private InputConnection mInputConnection;
+    @NonNull
+    private final AtomicReference<InputConnection> mInputConnectionRef;
 
     @NonNull
     private final Looper mLooper;
     private final Handler mH;
 
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private boolean mFinished = false;
-
     private final InputMethodManager mParentInputMethodManager;
     private final WeakReference<View> mServedView;
 
@@ -185,7 +180,7 @@
     RemoteInputConnectionImpl(@NonNull Looper looper,
             @NonNull InputConnection inputConnection,
             @NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
-        mInputConnection = inputConnection;
+        mInputConnectionRef = new AtomicReference<>(inputConnection);
         mLooper = looper;
         mH = new Handler(mLooper);
         mParentInputMethodManager = inputMethodManager;
@@ -197,9 +192,7 @@
      */
     @Nullable
     public InputConnection getInputConnection() {
-        synchronized (mLock) {
-            return mInputConnection;
-        }
+        return mInputConnectionRef.get();
     }
 
     /**
@@ -215,9 +208,7 @@
      * {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}.
      */
     private boolean isFinished() {
-        synchronized (mLock) {
-            return mFinished;
-        }
+        return mInputConnectionRef.get() == null;
     }
 
     private boolean isActive() {
@@ -386,10 +377,7 @@
                     // TODO(b/199934664): See if we can remove this by providing a default impl.
                 }
             } finally {
-                synchronized (mLock) {
-                    mInputConnection = null;
-                    mFinished = true;
-                }
+                mInputConnectionRef.set(null);
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
 
@@ -441,7 +429,6 @@
     public String toString() {
         return "RemoteInputConnectionImpl{"
                 + "connection=" + getInputConnection()
-                + " finished=" + isFinished()
                 + " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
                 + " mServedView=" + mServedView.get()
                 + "}";
@@ -455,16 +442,14 @@
      *                {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}.
      */
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
-        synchronized (mLock) {
-            // Check that the call is initiated in the target thread of the current InputConnection
-            // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
-            // executed on this thread. Otherwise the messages are dispatched to the correct thread
-            // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
-            // reasons.
-            if ((mInputConnection instanceof DumpableInputConnection)
-                    && mLooper.isCurrentThread()) {
-                ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
-            }
+        final InputConnection ic = mInputConnectionRef.get();
+        // Check that the call is initiated in the target thread of the current InputConnection
+        // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+        // executed on this thread. Otherwise the messages are dispatched to the correct thread
+        // in IInputConnectionWrapper, but this is not wanted while dumping, for performance
+        // reasons.
+        if ((ic instanceof DumpableInputConnection) && mLooper.isCurrentThread()) {
+            ((DumpableInputConnection) ic).dumpDebug(proto, fieldId);
         }
     }
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 66aa66c..b0e5f777 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -786,6 +786,42 @@
         }
     }
 
+    /**
+     * @hide
+     * @return True if there is a change
+     */
+    public boolean replaceRemoteCollections(int viewId) {
+        boolean isActionReplaced = false;
+        if (mActions != null) {
+            for (int i = 0; i < mActions.size(); i++) {
+                Action action = mActions.get(i);
+                if (action instanceof SetRemoteCollectionItemListAdapterAction itemsAction
+                        && itemsAction.viewId == viewId
+                        && itemsAction.mServiceIntent != null) {
+                    mActions.set(i, new SetRemoteCollectionItemListAdapterAction(itemsAction.viewId,
+                            itemsAction.mServiceIntent));
+                    isActionReplaced = true;
+                } else if (action instanceof ViewGroupActionAdd groupAction
+                        && groupAction.mNestedViews != null) {
+                    isActionReplaced |= groupAction.mNestedViews.replaceRemoteCollections(viewId);
+                }
+            }
+        }
+        if (mSizedRemoteViews != null) {
+            for (int i = 0; i < mSizedRemoteViews.size(); i++) {
+                isActionReplaced |= mSizedRemoteViews.get(i).replaceRemoteCollections(viewId);
+            }
+        }
+        if (mLandscape != null) {
+            isActionReplaced |= mLandscape.replaceRemoteCollections(viewId);
+        }
+        if (mPortrait != null) {
+            isActionReplaced |= mPortrait.replaceRemoteCollections(viewId);
+        }
+
+        return isActionReplaced;
+    }
+
     private static void visitIconUri(Icon icon, @NonNull Consumer<Uri> visitor) {
         if (icon != null && (icon.getType() == Icon.TYPE_URI
                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
@@ -1059,18 +1095,21 @@
 
     private class SetRemoteCollectionItemListAdapterAction extends Action {
         private @NonNull CompletableFuture<RemoteCollectionItems> mItemsFuture;
+        final Intent mServiceIntent;
 
         SetRemoteCollectionItemListAdapterAction(@IdRes int id,
                 @NonNull RemoteCollectionItems items) {
             viewId = id;
             items.setHierarchyRootData(getHierarchyRootData());
             mItemsFuture = CompletableFuture.completedFuture(items);
+            mServiceIntent = null;
         }
 
         SetRemoteCollectionItemListAdapterAction(@IdRes int id, Intent intent) {
             viewId = id;
             mItemsFuture = getItemsFutureFromIntentWithTimeout(intent);
             setHierarchyRootData(getHierarchyRootData());
+            mServiceIntent = intent;
         }
 
         private static CompletableFuture<RemoteCollectionItems> getItemsFutureFromIntentWithTimeout(
@@ -1119,6 +1158,7 @@
             viewId = parcel.readInt();
             mItemsFuture = CompletableFuture.completedFuture(
                     new RemoteCollectionItems(parcel, getHierarchyRootData()));
+            mServiceIntent = parcel.readTypedObject(Intent.CREATOR);
         }
 
         @Override
@@ -1148,6 +1188,7 @@
             dest.writeInt(viewId);
             RemoteCollectionItems items = getCollectionItemsFromFuture(mItemsFuture);
             items.writeToParcel(dest, flags, /* attached= */ true);
+            dest.writeTypedObject(mServiceIntent, flags);
         }
 
         @Override
@@ -4768,9 +4809,7 @@
      */
     @Deprecated
     public void setRemoteAdapter(@IdRes int viewId, Intent intent) {
-        if (AppGlobals.getIntCoreSetting(
-                SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION,
-                SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1) {
+        if (isAdapterConversionEnabled()) {
             addAction(new SetRemoteCollectionItemListAdapterAction(viewId, intent));
             return;
         }
@@ -4778,6 +4817,16 @@
     }
 
     /**
+     * @hide
+     * @return True if the remote adapter conversion is enabled
+     */
+    public static boolean isAdapterConversionEnabled() {
+        return AppGlobals.getIntCoreSetting(
+                SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION,
+                SystemUiDeviceConfigFlags.REMOTEVIEWS_ADAPTER_CONVERSION_DEFAULT ? 1 : 0) == 1;
+    }
+
+    /**
      * Creates a simple Adapter for the viewId specified. The viewId must point to an AdapterView,
      * ie. {@link ListView}, {@link GridView}, {@link StackView} or {@link AdapterViewAnimator}.
      * This is a simpler but less flexible approach to populating collection widgets. Its use is
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index bdbce13..be88e53 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.TestApi;
+import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.InputConfig;
@@ -78,14 +79,21 @@
          */
         public final boolean isVisible;
 
+        /**
+         * Return the transform to get the bounds from display space into window space.
+         */
+        @NonNull
+        public final Matrix transform;
+
         WindowInfo(@NonNull IBinder windowToken, @NonNull String name, int displayId,
-                @NonNull Rect bounds, int inputConfig) {
+                @NonNull Rect bounds, int inputConfig, @NonNull Matrix transform) {
             this.windowToken = windowToken;
             this.name = name;
             this.displayId = displayId;
             this.bounds = bounds;
             this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
             this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
+            this.transform = transform;
         }
 
         @Override
@@ -94,7 +102,8 @@
                     + ", frame=" + bounds
                     + ", isVisible=" + isVisible
                     + ", isTrustedOverlay=" + isTrustedOverlay
-                    + ", token=" + windowToken;
+                    + ", token=" + windowToken
+                    + ", transform=" + transform;
         }
     }
 
@@ -155,7 +164,7 @@
             var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
                     handle.frameBottom);
             windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, handle.displayId,
-                    bounds, handle.inputConfig));
+                    bounds, handle.inputConfig, handle.transform));
         }
         return windowInfos;
     }
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 632208c..849e0b3 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -23,6 +23,7 @@
 import android.content.ContextWrapper;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.res.TypedArray;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -33,6 +34,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -62,6 +64,9 @@
             .getInt("persist.wm.debug.predictive_back", 1) != 0;
     private static final boolean ALWAYS_ENFORCE_PREDICTIVE_BACK = SystemProperties
             .getInt("persist.wm.debug.predictive_back_always_enforce", 0) != 0;
+    private static final boolean PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE =
+            SystemProperties.getInt("persist.wm.debug.predictive_back_fallback_window_attribute", 0)
+                    != 0;
     @Nullable
     private ImeOnBackInvokedDispatcher mImeDispatcher;
 
@@ -500,6 +505,31 @@
                             applicationInfo.packageName,
                             requestsPredictiveBack));
                 }
+
+                if (PREDICTIVE_BACK_FALLBACK_WINDOW_ATTRIBUTE && !requestsPredictiveBack) {
+                    // Compatibility check for legacy window style flag used by Wear OS.
+                    // Note on compatibility behavior:
+                    // 1. windowSwipeToDismiss should be respected for all apps not opted in.
+                    // 2. windowSwipeToDismiss should be true for all apps not opted in, which
+                    //    enables the PB animation for them.
+                    // 3. windowSwipeToDismiss=false should be respected for apps not opted in,
+                    //    which disables PB & onBackPressed caused by BackAnimController's
+                    //    setTrigger(true)
+                    TypedArray windowAttr =
+                            context.obtainStyledAttributes(
+                                    new int[] {android.R.attr.windowSwipeToDismiss});
+                    boolean windowSwipeToDismiss = true;
+                    if (windowAttr.getIndexCount() > 0) {
+                        windowSwipeToDismiss = windowAttr.getBoolean(0, true);
+                    }
+                    windowAttr.recycle();
+
+                    if (DEBUG) {
+                        Log.i(TAG, "falling back to windowSwipeToDismiss: " + windowSwipeToDismiss);
+                    }
+
+                    requestsPredictiveBack = windowSwipeToDismiss;
+                }
             }
 
             return requestsPredictiveBack;
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index 55b823b..47d3df87 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -20,7 +20,6 @@
 import static android.window.ConfigurationHelper.shouldUpdateResources;
 
 import android.annotation.AnyThread;
-import android.annotation.BinderThread;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.app.ActivityThread;
@@ -97,9 +96,10 @@
      * @param newConfig the updated {@link Configuration}
      * @param newDisplayId the updated {@link android.view.Display} ID
      */
-    @BinderThread
+    @AnyThread
     @Override
     public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+        // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
         mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
                 newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
     }
@@ -188,9 +188,10 @@
         }
     }
 
-    @BinderThread
+    @AnyThread
     @Override
     public void onWindowTokenRemoved() {
+        // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
         mHandler.post(PooledLambda.obtainRunnable(
                 WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
     }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
index 81cd280..10336bd 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java
@@ -66,10 +66,6 @@
         public static final Flag SHOW_STICKY_HUN_FOR_DENIED_FSI =
                 releasedFlag("persist.sysui.notification.show_sticky_hun_for_denied_fsi");
 
-        /** Gating the ability for users to dismiss ongoing event notifications */
-        public static final Flag ALLOW_DISMISS_ONGOING =
-                releasedFlag("persist.sysui.notification.ongoing_dismissal");
-
         /** Gating the redaction of OTP notifications on the lockscreen */
         public static final Flag OTP_REDACTION =
                 devFlag("persist.sysui.notification.otp_redaction");
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 66e3333..8a5c7ef 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 import android.view.View;
 import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.annotations.GuardedBy;
@@ -253,13 +254,11 @@
     /**
      * Calls {@link IInputMethodPrivilegedOperations#hideMySoftInput(int, int, AndroidFuture)}
      *
-     * @param flags additional operating flags
      * @param reason the reason to hide soft input
-     * @see android.view.inputmethod.InputMethodManager#HIDE_IMPLICIT_ONLY
-     * @see android.view.inputmethod.InputMethodManager#HIDE_NOT_ALWAYS
      */
     @AnyThread
-    public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason) {
+    public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
+            @SoftInputShowHideReason int reason) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
@@ -275,13 +274,9 @@
 
     /**
      * Calls {@link IInputMethodPrivilegedOperations#showMySoftInput(int, AndroidFuture)}
-     *
-     * @param flags additional operating flags
-     * @see android.view.inputmethod.InputMethodManager#SHOW_IMPLICIT
-     * @see android.view.inputmethod.InputMethodManager#SHOW_FORCED
      */
     @AnyThread
-    public void showMySoftInput(int flags) {
+    public void showMySoftInput(@InputMethodManager.ShowFlags int flags) {
         final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
         if (ops == null) {
             return;
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index c2cfcd6..66b0158 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1565,6 +1565,13 @@
         mInStealthMode = ss.isInStealthMode();
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+
+        setSystemGestureExclusionRects(List.of(new Rect(left, top, right, bottom)));
+    }
+
     /**
      * The parecelable for saving and restoring a lock pattern view.
      */
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 15270ef..061f669 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -18,10 +18,10 @@
 
 //#define LOG_NDEBUG 0
 
+#include <android-base/logging.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <input/InputTransport.h>
 #include <inttypes.h>
-#include <log/log.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <utils/Looper.h>
@@ -91,7 +91,8 @@
         mMessageQueue(messageQueue),
         mNextPublishedSeq(1) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName().c_str());
+        LOG(DEBUG) << "channel '" << getInputChannelName()
+                   << "' ~ Initializing input event sender.";
     }
 }
 
@@ -108,7 +109,7 @@
 
 void NativeInputEventSender::dispose() {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName().c_str());
+        LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Disposing input event sender.";
     }
 
     mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
@@ -116,7 +117,7 @@
 
 status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName().c_str(), seq);
+        LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Sending key event, seq=" << seq;
     }
 
     uint32_t publishedSeq = mNextPublishedSeq++;
@@ -128,8 +129,8 @@
                                             event->getMetaState(), event->getRepeatCount(),
                                             event->getDownTime(), event->getEventTime());
     if (status) {
-        ALOGW("Failed to send key event on channel '%s'.  status=%d",
-                getInputChannelName().c_str(), status);
+        LOG(WARNING) << "Failed to send key event on channel '" << getInputChannelName()
+                     << "'.  status=" << statusToString(status);
         return status;
     }
     mPublishedSeqMap.emplace(publishedSeq, seq);
@@ -138,7 +139,8 @@
 
 status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName().c_str(), seq);
+        LOG(DEBUG) << "channel '" << getInputChannelName()
+                   << "' ~ Sending motion event, seq=" << seq;
     }
 
     uint32_t publishedSeq;
@@ -162,8 +164,8 @@
                                                    event->getPointerProperties(),
                                                    event->getHistoricalRawPointerCoords(0, i));
         if (status) {
-            ALOGW("Failed to send motion event sample on channel '%s'.  status=%d",
-                    getInputChannelName().c_str(), status);
+            LOG(WARNING) << "Failed to send motion event sample on channel '"
+                         << getInputChannelName() << "'.  status=" << statusToString(status);
             return status;
         }
         // mPublishedSeqMap tracks all sequences published from this sender. Only the last
@@ -183,16 +185,18 @@
         // as part of finishing an IME session, in which case the publisher will
         // soon be disposed as well.
         if (kDebugDispatchCycle) {
-            ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred.  events=0x%x",
-                  getInputChannelName().c_str(), events);
+            LOG(DEBUG) << "channel '" << getInputChannelName()
+                       << "' ~ Consumer closed input channel or an error occurred.  events=0x"
+                       << std::hex << events;
         }
 
         return 0; // remove the callback
     }
 
     if (!(events & ALOOPER_EVENT_INPUT)) {
-        ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  events=0x%x",
-              getInputChannelName().c_str(), events);
+        LOG(WARNING) << "channel '" << getInputChannelName()
+                     << "' ~ Received spurious callback for unhandled poll event.  events=0x"
+                     << std::hex << events;
         return 1;
     }
 
@@ -204,13 +208,13 @@
 
 status_t NativeInputEventSender::processConsumerResponse(JNIEnv* env) {
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName().c_str());
+        LOG(DEBUG) << "channel '" << getInputChannelName() << "' ~ Receiving finished signals.";
     }
 
     ScopedLocalRef<jobject> senderObj(env, GetReferent(env, mSenderWeakGlobal));
     if (!senderObj.get()) {
-        ALOGW("channel '%s' ~ Sender object was finalized without being disposed.",
-              getInputChannelName().c_str());
+        LOG(WARNING) << "channel '" << getInputChannelName()
+                     << "' ~ Sender object was finalized without being disposed.";
         return DEAD_OBJECT;
     }
     bool skipCallbacks = false; // stop calling Java functions after an exception occurs
@@ -221,8 +225,9 @@
             if (status == WOULD_BLOCK) {
                 return OK;
             }
-            ALOGE("channel '%s' ~ Failed to process consumer response.  status=%d",
-                  getInputChannelName().c_str(), status);
+            LOG(ERROR) << "channel '" << getInputChannelName()
+                       << "' ~ Failed to process consumer response.  status="
+                       << statusToString(status);
             return status;
         }
 
@@ -250,24 +255,25 @@
         const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(response);
 
         if (kDebugDispatchCycle) {
-            ALOGD("channel '%s' ~ Received timeline, inputEventId=%" PRId32
-                  ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
-                  getInputChannelName().c_str(), timeline.inputEventId,
-                  timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
-                  timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+            LOG(DEBUG) << "channel '" << getInputChannelName()
+                       << "' ~ Received timeline, inputEventId=" << timeline.inputEventId
+                       << ", gpuCompletedTime="
+                       << timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]
+                       << ", presentTime="
+                       << timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
         }
 
         if (skipCallbacks) {
-            ALOGW("Java exception occurred. Skipping dispatchTimelineReported for "
-                  "inputEventId=%" PRId32,
-                  timeline.inputEventId);
+            LOG(WARNING) << "Java exception occurred. Skipping dispatchTimelineReported for "
+                            "inputEventId="
+                         << timeline.inputEventId;
             return true;
         }
 
         env->CallVoidMethod(sender, gInputEventSenderClassInfo.dispatchTimelineReported,
                             timeline.inputEventId, timeline.graphicsTimeline);
         if (env->ExceptionCheck()) {
-            ALOGE("Exception dispatching timeline, inputEventId=%" PRId32, timeline.inputEventId);
+            LOG(ERROR) << "Exception dispatching timeline, inputEventId=" << timeline.inputEventId;
             return false;
         }
 
@@ -279,7 +285,7 @@
 
     auto it = mPublishedSeqMap.find(finished.seq);
     if (it == mPublishedSeqMap.end()) {
-        ALOGW("Received 'finished' signal for unknown seq number = %" PRIu32, finished.seq);
+        LOG(WARNING) << "Received 'finished' signal for unknown seq number = " << finished.seq;
         // Since this is coming from the receiver (typically app), it's possible that an app
         // does something wrong and sends bad data. Just ignore and process other events.
         return true;
@@ -296,9 +302,9 @@
     const uint32_t seq = seqOptional.value();
 
     if (kDebugDispatchCycle) {
-        ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, pendingEvents=%zu.",
-              getInputChannelName().c_str(), seq, finished.handled ? "true" : "false",
-              mPublishedSeqMap.size());
+        LOG(DEBUG) << "channel '" << getInputChannelName()
+                   << "' ~ Received finished signal, seq=" << seq << ", handled=" << std::boolalpha
+                   << finished.handled << ", pendingEvents=" << mPublishedSeqMap.size();
     }
     if (skipCallbacks) {
         return true;
@@ -307,7 +313,7 @@
     env->CallVoidMethod(sender, gInputEventSenderClassInfo.dispatchInputEventFinished,
                         static_cast<jint>(seq), static_cast<jboolean>(finished.handled));
     if (env->ExceptionCheck()) {
-        ALOGE("Exception dispatching finished signal for seq=%" PRIu32, seq);
+        LOG(ERROR) << "Exception dispatching finished signal for seq=" << seq;
         return false;
     }
     return true;
diff --git a/core/res/res/values-watch/config_material.xml b/core/res/res/values-watch/config_material.xml
index 03d3637..529f18b 100644
--- a/core/res/res/values-watch/config_material.xml
+++ b/core/res/res/values-watch/config_material.xml
@@ -30,9 +30,6 @@
     <!-- Always overscan by default to ensure onApplyWindowInsets will always be called. -->
     <bool name="config_windowOverscanByDefault">true</bool>
 
-    <!-- Enable windowSwipeToDismiss. -->
-    <bool name="config_windowSwipeToDismiss">true</bool>
-
     <!-- Style the scrollbars accoridngly. -->
     <drawable name="config_scrollbarThumbVertical">@drawable/scrollbar_vertical_thumb</drawable>
     <drawable name="config_scrollbarTrackVertical">@drawable/scrollbar_vertical_track</drawable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 71da8b1..7602f69 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2624,6 +2624,17 @@
          assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
     <bool name="config_dismissDreamOnActivityStart">false</bool>
 
+    <!-- Whether to send a user activity event to PowerManager when a dream quits unexpectedly so
+         that the screen won't immediately shut off.
+
+         When a dream stops unexpectedly, such as due to an app update, if the device has been
+         inactive less than the user's screen timeout, the device goes to keyguard and times out
+         back to dreaming after a few seconds. If the device has been inactive longer, the screen
+         will immediately turn off. With this flag on, the device will go back to keyguard in all
+         scenarios rather than turning off, which gives the device a chance to start dreaming
+         again. -->
+    <bool name="config_resetScreenTimeoutOnUnexpectedDreamExit">false</bool>
+
     <!-- The prefixes of dream component names that are loggable.
          Matched against ComponentName#flattenToString() for dream components.
          If empty, logs "other" for all. -->
@@ -5606,7 +5617,7 @@
         the Option 3 is selected for R.integer.config_letterboxBackgroundType.
         Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. -->
     <item name="config_letterboxBackgroundWallaperDarkScrimAlpha" format="float" type="dimen">
-        0.68
+        0.75
     </item>
 
     <!-- Corners appearance of the letterbox background.
@@ -5631,7 +5642,7 @@
             but isn't supported on the device or both dark scrim alpha and blur radius aren't
             provided.
      -->
-    <color name="config_letterboxBackgroundColor">@color/system_on_secondary_fixed</color>
+    <color name="config_letterboxBackgroundColor">@color/system_neutral1_1000</color>
 
     <!-- Horizontal position of a center of the letterboxed app window.
         0 corresponds to the left side of the screen and 1 to the right side. If given value < 0
@@ -6545,4 +6556,12 @@
          serialization, a default vibration will be used.
          Note that, indefinitely repeating vibrations are not allowed as shutdown vibrations. -->
     <string name="config_defaultShutdownVibrationFile" />
+    <!-- The file path in which custom vibrations are provided for haptic feedbacks.
+         If the device does not specify any such file path here, if the file path specified here
+         does not exist, or if the contents of the file does not make up a valid customization
+         serialization, the system default vibrations for haptic feedback will be used.
+         If the content of the customization file is valid, the system will use the provided
+         vibrations for the customized haptic feedback IDs, and continue to use the system defaults
+         for the non-customized ones. -->
+    <string name="config_hapticFeedbackCustomizationFile" />
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 804154d..978849d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2219,6 +2219,7 @@
   <java-symbol type="array" name="config_supportedDreamComplications" />
   <java-symbol type="array" name="config_disabledDreamComponents" />
   <java-symbol type="bool" name="config_dismissDreamOnActivityStart" />
+  <java-symbol type="bool" name="config_resetScreenTimeoutOnUnexpectedDreamExit" />
   <java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
   <java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
   <java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
@@ -5177,4 +5178,5 @@
   <java-symbol type="drawable" name="focus_event_pressed_key_background" />
   <java-symbol type="string" name="config_defaultShutdownVibrationFile" />
   <java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />
+  <java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />
 </resources>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4857741..c1b55cd 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,6 +21,7 @@
 import static android.content.Intent.ACTION_VIEW;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +30,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.annotation.Nullable;
 import android.app.Activity;
@@ -46,6 +49,7 @@
 import android.app.servertransaction.NewIntentItem;
 import android.app.servertransaction.ResumeActivityItem;
 import android.app.servertransaction.StopActivityItem;
+import android.app.servertransaction.WindowTokenClientController;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.CompatibilityInfo;
@@ -70,6 +74,7 @@
 import com.android.internal.content.ReferrerIntent;
 
 import org.junit.After;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -83,7 +88,7 @@
 /**
  * Test for verifying {@link android.app.ActivityThread} class.
  * Build/Install/Run:
- *  atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
+ *  atest FrameworksCoreTests:ActivityThreadTest
  */
 @RunWith(AndroidJUnit4.class)
 @MediumTest
@@ -100,14 +105,24 @@
             new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
                     false /* launchActivity */);
 
+    private WindowTokenClientController mOriginalWindowTokenClientController;
+
     private ArrayList<VirtualDisplay> mCreatedVirtualDisplays;
 
+    @Before
+    public void setup() {
+        // Keep track of the original controller, so that it can be used to restore in tearDown()
+        // when there is override in some test cases.
+        mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
+    }
+
     @After
     public void tearDown() {
         if (mCreatedVirtualDisplays != null) {
             mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
             mCreatedVirtualDisplays = null;
         }
+        WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
     }
 
     @Test
@@ -730,6 +745,39 @@
         assertFalse(activity.enterPipSkipped());
     }
 
+    @Test
+    public void testHandleWindowContextConfigurationChanged() {
+        final Activity activity = mActivityTestRule.launchActivity(new Intent());
+        final ActivityThread activityThread = activity.getActivityThread();
+        final WindowTokenClientController windowTokenClientController =
+                mock(WindowTokenClientController.class);
+        WindowTokenClientController.overrideForTesting(windowTokenClientController);
+        final IBinder clientToken = mock(IBinder.class);
+        final Configuration configuration = new Configuration();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+                .handleWindowContextConfigurationChanged(
+                        clientToken, configuration, DEFAULT_DISPLAY));
+
+        verify(windowTokenClientController).onWindowContextConfigurationChanged(
+                clientToken, configuration, DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public void testHandleWindowContextWindowRemoval() {
+        final Activity activity = mActivityTestRule.launchActivity(new Intent());
+        final ActivityThread activityThread = activity.getActivityThread();
+        final WindowTokenClientController windowTokenClientController =
+                mock(WindowTokenClientController.class);
+        WindowTokenClientController.overrideForTesting(windowTokenClientController);
+        final IBinder clientToken = mock(IBinder.class);
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+                .handleWindowContextWindowRemoval(clientToken));
+
+        verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
+    }
+
     /**
      * Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
      * Configuration, int)} to try to push activity configuration to the activity for the given
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
new file mode 100644
index 0000000..7811e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextConfigurationChangeItem}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ */
+public class WindowContextConfigurationChangeItemTest {
+
+    @Mock
+    private ClientTransactionHandler mHandler;
+    @Mock
+    private IBinder mToken;
+    @Mock
+    private PendingTransactionActions mPendingActions;
+    @Mock
+    private IBinder mClientToken;
+    // Can't mock final class.
+    private final Configuration mConfiguration = new Configuration();
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testExecute() {
+        final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+                .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
+        item.execute(mHandler, mToken, mPendingActions);
+
+        verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
+                DEFAULT_DISPLAY);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
new file mode 100644
index 0000000..2c83c70
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.servertransaction;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextWindowRemovalItem}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
+ */
+public class WindowContextWindowRemovalItemTest {
+
+    @Mock
+    private ClientTransactionHandler mHandler;
+    @Mock
+    private IBinder mToken;
+    @Mock
+    private PendingTransactionActions mPendingActions;
+    @Mock
+    private IBinder mClientToken;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void testExecute() {
+        final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+                mClientToken);
+        item.execute(mHandler, mToken, mPendingActions);
+
+        verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
+    }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
new file mode 100644
index 0000000..3b2fe58
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
@@ -0,0 +1,215 @@
+/*
+ * 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.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowTokenClientController}.
+ *
+ * Build/Install/Run:
+ *  atest FrameworksCoreTests:WindowTokenClientControllerTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTokenClientControllerTest {
+
+    @Mock
+    private IWindowManager mWindowManagerService;
+    @Mock
+    private WindowTokenClient mWindowTokenClient;
+    @Mock
+    private IBinder mClientToken;
+    @Mock
+    private IBinder mWindowToken;
+    // Can't mock final class.
+    private final Configuration mConfiguration = new Configuration();
+
+    private IWindowManager mOriginalWindowManagerService;
+
+    private WindowTokenClientController mController;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mOriginalWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+        WindowManagerGlobal.overrideWindowManagerServiceForTesting(mWindowManagerService);
+        doReturn(mClientToken).when(mWindowTokenClient).asBinder();
+        mController = spy(WindowTokenClientController.getInstance());
+    }
+
+    @After
+    public void tearDown() {
+        WindowManagerGlobal.overrideWindowManagerServiceForTesting(mOriginalWindowManagerService);
+    }
+
+    @Test
+    public void testAttachToDisplayArea() throws RemoteException {
+        doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+                any(), anyInt(), anyInt(), any());
+
+        assertFalse(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+                DEFAULT_DISPLAY, null /* options */));
+        verify(mWindowManagerService).attachWindowContextToDisplayArea(mWindowTokenClient,
+                TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
+        verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+                any(), anyInt(), anyInt(), any());
+
+        assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+                DEFAULT_DISPLAY, null /* options */));
+        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+                false /* shouldReportConfigChange */);
+    }
+
+    @Test
+    public void testAttachToDisplayArea_detachIfNeeded() throws RemoteException {
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+        doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+                any(), anyInt(), anyInt(), any());
+        mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+                DEFAULT_DISPLAY, null /* options */);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+        doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+                any(), anyInt(), anyInt(), any());
+        mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+                DEFAULT_DISPLAY, null /* options */);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+    }
+
+    @Test
+    public void testAttachToDisplayContent() throws RemoteException {
+        doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+                any(), anyInt());
+
+        assertFalse(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+        verify(mWindowManagerService).attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+        verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+        doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+                any(), anyInt());
+
+        assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+                false /* shouldReportConfigChange */);
+    }
+
+    @Test
+    public void testAttachToDisplayContent_detachIfNeeded() throws RemoteException {
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+        doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+                any(), anyInt());
+        mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+        doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+                any(), anyInt());
+        mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+    }
+
+    @Test
+    public void testAttachToWindowToken() throws RemoteException {
+        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+        verify(mWindowManagerService).attachWindowContextToWindowToken(mWindowTokenClient,
+                mWindowToken);
+    }
+
+    @Test
+    public void testAttachToWindowToken_detachIfNeeded() throws RemoteException {
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+        mController.detachIfNeeded(mWindowTokenClient);
+
+        verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+    }
+
+    @Test
+    public void testOnWindowContextConfigurationChanged() {
+        mController.onWindowContextConfigurationChanged(
+                mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+        verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
+
+        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+        mController.onWindowContextConfigurationChanged(
+                mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+        verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+    }
+
+    @Test
+    public void testOnWindowContextWindowRemoved() {
+        mController.onWindowContextWindowRemoved(mClientToken);
+
+        verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+        mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+        mController.onWindowContextWindowRemoved(mClientToken);
+
+        verify(mWindowTokenClient).onWindowTokenRemoved();
+    }
+}
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
new file mode 100644
index 0000000..bbc2458
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.content.ContentCaptureOptionsTest"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..f8beac2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.view.contentcapture"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..3cd4e17
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.view.contentprotection"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index 467d555..5f2aecc 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -37,6 +37,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -62,16 +63,24 @@
     @Mock
     private WindowTokenClient mMockToken;
 
+    private WindowTokenClientController mOriginalController;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         mController = new WindowContextController(mMockToken);
         doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
-        WindowTokenClientController.overrideInstance(mWindowTokenClientController);
+        mOriginalController = WindowTokenClientController.getInstance();
+        WindowTokenClientController.overrideForTesting(mWindowTokenClientController);
         doReturn(true).when(mWindowTokenClientController).attachToDisplayArea(
                 eq(mMockToken), anyInt(), anyInt(), any());
     }
 
+    @After
+    public void tearDown() {
+        WindowTokenClientController.overrideForTesting(mOriginalController);
+    }
+
     @Test(expected = IllegalStateException.class)
     public void testAttachToDisplayAreaTwiceThrowException() {
         mController.attachToDisplayArea(TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY,
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
new file mode 100644
index 0000000..829409a
--- /dev/null
+++ b/core/tests/vibrator/Android.bp
@@ -0,0 +1,40 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "FrameworksVibratorCoreTests",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "frameworks-base-testutils",
+        "guava",
+        "androidx.core_core",
+        "androidx.test.ext.junit",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "mockito-target-minus-junit4",
+        "truth-prebuilt",
+        "testng",
+    ],
+
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+        "framework",
+        "framework-res",
+    ],
+
+    sdk_version: "core_platform",
+    test_suites: [
+        "device-tests",
+        "automotive-tests",
+    ],
+
+    certificate: "platform",
+}
diff --git a/core/tests/vibrator/AndroidManifest.xml b/core/tests/vibrator/AndroidManifest.xml
new file mode 100644
index 0000000..1ce6071
--- /dev/null
+++ b/core/tests/vibrator/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.frameworks.core.tests.vibrator">
+
+    <!-- vibrator test permissions -->
+    <uses-permission android:name="android.permission.VIBRATE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.frameworks.core.tests.vibrator"
+            android:label="Frameworks Vibrator Core Tests" />
+</manifest>
diff --git a/core/tests/coretests/src/android/os/vibrator/OWNERS b/core/tests/vibrator/OWNERS
similarity index 78%
rename from core/tests/coretests/src/android/os/vibrator/OWNERS
rename to core/tests/vibrator/OWNERS
index b54d6bf..00446f2 100644
--- a/core/tests/coretests/src/android/os/vibrator/OWNERS
+++ b/core/tests/vibrator/OWNERS
@@ -1 +1,2 @@
+# Bug component: 345036
 include platform/frameworks/base:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/core/tests/vibrator/TEST_MAPPING b/core/tests/vibrator/TEST_MAPPING
new file mode 100644
index 0000000..f3333d8
--- /dev/null
+++ b/core/tests/vibrator/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksVibratorCoreTests",
+      "options": [
+        {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksVibratorCoreTests",
+      "options": [
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ]
+}
+
diff --git a/core/tests/coretests/src/android/os/CombinedVibrationTest.java b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/CombinedVibrationTest.java
rename to core/tests/vibrator/src/android/os/CombinedVibrationTest.java
index 508856b..244fcff 100644
--- a/core/tests/coretests/src/android/os/CombinedVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/CombinedVibrationTest.java
@@ -22,8 +22,6 @@
 
 import static org.testng.Assert.assertThrows;
 
-import android.platform.test.annotations.Presubmit;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -31,7 +29,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
-@Presubmit
 @RunWith(JUnit4.class)
 public class CombinedVibrationTest {
     private static final VibrationEffect VALID_EFFECT = VibrationEffect.createOneShot(10, 255);
diff --git a/core/tests/coretests/src/android/os/ExternalVibrationTest.java b/core/tests/vibrator/src/android/os/ExternalVibrationTest.java
similarity index 96%
rename from core/tests/coretests/src/android/os/ExternalVibrationTest.java
rename to core/tests/vibrator/src/android/os/ExternalVibrationTest.java
index 3b872d5..587594d 100644
--- a/core/tests/coretests/src/android/os/ExternalVibrationTest.java
+++ b/core/tests/vibrator/src/android/os/ExternalVibrationTest.java
@@ -22,12 +22,14 @@
 
 import android.media.AudioAttributes;
 
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ExternalVibrationTest {
+    @Ignore("b/291713224")
     @Test
     public void testSerialization() {
         AudioAttributes audio = new AudioAttributes.Builder().build();
diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/VibrationEffectTest.java
rename to core/tests/vibrator/src/android/os/VibrationEffectTest.java
index 73954da..8be489e 100644
--- a/core/tests/coretests/src/android/os/VibrationEffectTest.java
+++ b/core/tests/vibrator/src/android/os/VibrationEffectTest.java
@@ -40,7 +40,6 @@
 import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException;
 import android.os.vibrator.PrimitiveSegment;
 import android.os.vibrator.StepSegment;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -54,7 +53,6 @@
 import java.time.Duration;
 import java.util.Arrays;
 
-@Presubmit
 @RunWith(MockitoJUnitRunner.class)
 public class VibrationEffectTest {
 
diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/VibratorInfoTest.java
rename to core/tests/vibrator/src/android/os/VibratorInfoTest.java
index 88766e2..ff917aa 100644
--- a/core/tests/coretests/src/android/os/VibratorInfoTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorInfoTest.java
@@ -24,13 +24,11 @@
 
 import android.hardware.vibrator.Braking;
 import android.hardware.vibrator.IVibrator;
-import android.platform.test.annotations.Presubmit;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-@Presubmit
 @RunWith(JUnit4.class)
 public class VibratorInfoTest {
     private static final float TEST_TOLERANCE = 1e-5f;
diff --git a/core/tests/coretests/src/android/os/VibratorTest.java b/core/tests/vibrator/src/android/os/VibratorTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/VibratorTest.java
rename to core/tests/vibrator/src/android/os/VibratorTest.java
index 375fdac..c559e34 100644
--- a/core/tests/coretests/src/android/os/VibratorTest.java
+++ b/core/tests/vibrator/src/android/os/VibratorTest.java
@@ -40,7 +40,6 @@
 import android.hardware.vibrator.IVibrator;
 import android.media.AudioAttributes;
 import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -55,13 +54,6 @@
 import org.mockito.InOrder;
 import org.mockito.junit.MockitoJUnitRunner;
 
-/**
- * Tests for {@link Vibrator}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:VibratorTest
- */
-@Presubmit
 @RunWith(MockitoJUnitRunner.class)
 public class VibratorTest {
 
diff --git a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
index 9099274..3231192 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrebakedSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrebakedSegmentTest.java
@@ -29,7 +29,6 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.VibratorInfo;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
 
-@Presubmit
 @RunWith(MockitoJUnitRunner.class)
 public class PrebakedSegmentTest {
 
diff --git a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
index 298438f..955d6ac 100644
--- a/core/tests/coretests/src/android/os/vibrator/PrimitiveSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/PrimitiveSegmentTest.java
@@ -29,7 +29,6 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.os.VibratorInfo;
-import android.platform.test.annotations.Presubmit;
 
 import androidx.test.InstrumentationRegistry;
 
@@ -37,7 +36,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
 
-@Presubmit
 @RunWith(MockitoJUnitRunner.class)
 public class PrimitiveSegmentTest {
     private static final float TOLERANCE = 1e-2f;
diff --git a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
index 6f8c205..dcbb56e 100644
--- a/core/tests/coretests/src/android/os/vibrator/RampSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/RampSegmentTest.java
@@ -29,7 +29,6 @@
 import android.os.Parcel;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.platform.test.annotations.Presubmit;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -39,7 +38,6 @@
 import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.junit.MockitoRule;
 
-@Presubmit
 @RunWith(MockitoJUnitRunner.class)
 public class RampSegmentTest {
     private static final float TOLERANCE = 1e-2f;
diff --git a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
similarity index 99%
rename from core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
rename to core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
index ade2161..f9f1c08 100644
--- a/core/tests/coretests/src/android/os/vibrator/StepSegmentTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/StepSegmentTest.java
@@ -27,7 +27,6 @@
 import android.os.Parcel;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.platform.test.annotations.Presubmit;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -37,7 +36,6 @@
 import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.junit.MockitoRule;
 
-@Presubmit
 @RunWith(MockitoJUnitRunner.class)
 public class StepSegmentTest {
     private static final float TOLERANCE = 1e-2f;
diff --git a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
similarity index 70%
rename from core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
rename to core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
index c0fdf9f..ce17170 100644
--- a/core/tests/coretests/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
+++ b/core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java
@@ -28,11 +28,14 @@
 
 import android.os.VibrationEffect;
 import android.os.vibrator.PrebakedSegment;
-import android.platform.test.annotations.Presubmit;
+import android.util.Xml;
+
+import com.android.modules.utils.TypedXmlPullParser;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.xmlpull.v1.XmlPullParser;
 
 import java.io.IOException;
 import java.io.StringReader;
@@ -46,7 +49,6 @@
  * <p>The {@link VibrationEffect} public APIs are covered by CTS to enforce the schema defined at
  * services/core/xsd/vibrator/vibration/vibration.xsd.
  */
-@Presubmit
 @RunWith(JUnit4.class)
 public class VibrationEffectXmlSerializationTest {
 
@@ -65,6 +67,61 @@
     }
 
     @Test
+    public void testParseTag_succeedAndParserPointsToEndVibrationTag() throws Exception {
+        VibrationEffect effect = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_CLICK)
+                .addPrimitive(PRIMITIVE_TICK, 0.2497f)
+                .compose();
+        String xml = "<vibration>"
+                + "<primitive-effect name=\"click\"/>"
+                + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
+                + "</vibration>";
+        VibrationEffect effect2 = VibrationEffect.startComposition()
+                .addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
+                .addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
+                .compose();
+        String xml2 = "<vibration>"
+                + "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
+                + "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
+                + "</vibration>";
+
+        TypedXmlPullParser parser = createXmlPullParser(xml);
+        assertParseTagSucceeds(parser, effect);
+        parser.next();
+        assertEndOfDocument(parser);
+
+        // Test no-issues when an end-tag follows the vibration XML.
+        // To test this, starting with the corresponding "start-tag" is necessary.
+        parser = createXmlPullParser("<next-tag>" + xml + "</next-tag>");
+        // Move the parser once to point to the "<vibration> tag.
+        parser.next();
+        assertParseTagSucceeds(parser, effect);
+        parser.next();
+        assertEndTag(parser, "next-tag");
+
+        parser = createXmlPullParser(xml + "<next-tag>");
+        assertParseTagSucceeds(parser, effect);
+        parser.next();
+        assertStartTag(parser, "next-tag");
+
+        parser = createXmlPullParser(xml + xml2);
+        assertParseTagSucceeds(parser, effect);
+        parser.next();
+        assertParseTagSucceeds(parser, effect2);
+        parser.next();
+        assertEndOfDocument(parser);
+    }
+
+    @Test
+    public void testParseTag_badXml_throwsException() throws Exception {
+        assertParseTagFails(
+                "<vibration>random text<primitive-effect name=\"click\"/></vibration>");
+        assertParseTagFails("<bad-tag><primitive-effect name=\"click\"/></vibration>");
+        assertParseTagFails("<primitive-effect name=\"click\"/></vibration>");
+        assertParseTagFails("<vibration><primitive-effect name=\"click\"/>");
+    }
+
+    @Test
     public void testPrimitives_allSucceed() throws IOException {
         VibrationEffect effect = VibrationEffect.startComposition()
                 .addPrimitive(PRIMITIVE_CLICK)
@@ -172,6 +229,41 @@
         assertThat(parse(xml, /* flags= */ 0)).isEqualTo(effect);
     }
 
+    private TypedXmlPullParser createXmlPullParser(String xml) throws Exception {
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(new StringReader(xml));
+        parser.next(); // read START_DOCUMENT
+        return parser;
+    }
+
+    /**
+     * Asserts parsing vibration from an open TypedXmlPullParser succeeds, and that the parser
+     * points to the end "vibration" tag.
+     */
+    private void assertParseTagSucceeds(
+            TypedXmlPullParser parser, VibrationEffect effect) throws Exception {
+        assertThat(parseTag(parser)).isEqualTo(effect);
+
+        assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG);
+        assertThat(parser.getName()).isEqualTo("vibration");
+    }
+
+    private void assertEndTag(TypedXmlPullParser parser, String expectedTagName) throws Exception {
+        assertThat(parser.getName()).isEqualTo(expectedTagName);
+        assertThat(parser.getEventType()).isEqualTo(parser.END_TAG);
+    }
+
+    private void assertStartTag(TypedXmlPullParser parser, String expectedTagName)
+            throws Exception {
+        assertThat(parser.getName()).isEqualTo(expectedTagName);
+        assertThat(parser.getEventType()).isEqualTo(parser.START_TAG);
+    }
+
+    private void assertEndOfDocument(TypedXmlPullParser parser) throws Exception {
+        assertThat(parser.getEventType()).isEqualTo(parser.END_DOCUMENT);
+    }
+
     private void assertHiddenApisParserSucceeds(String xml, VibrationEffect effect)
             throws IOException {
         assertThat(parse(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)).isEqualTo(effect);
@@ -183,6 +275,12 @@
                 () -> serialize(effect, /* flags= */ 0));
     }
 
+    private void assertParseTagFails(String xml) {
+        assertThrows("Expected parsing to fail for " + xml,
+                VibrationXmlParser.VibrationXmlParserException.class,
+                () -> parseTag(createXmlPullParser(xml)));
+    }
+
     private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
             String... expectedSegments) throws IOException {
         assertSerializationContainsSegments(serialize(effect, /* flags= */ 0), expectedSegments);
@@ -214,6 +312,11 @@
         return VibrationXmlParser.parse(new StringReader(xml), flags);
     }
 
+    private static VibrationEffect parseTag(TypedXmlPullParser parser)
+            throws IOException, VibrationXmlParser.VibrationXmlParserException {
+        return VibrationXmlParser.parseTag(parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+    }
+
     private static String serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags)
             throws IOException {
         StringWriter writer = new StringWriter();
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index aa0a8d9..3206dd2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -257,6 +257,7 @@
         <permission name="android.permission.CLEAR_APP_CACHE"/>
         <permission name="android.permission.ACCESS_INSTANT_APPS" />
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
         <permission name="android.permission.DELETE_CACHE_FILES"/>
         <permission name="android.permission.DELETE_PACKAGES"/>
         <permission name="android.permission.DUMP"/>
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 2b1515a..3bb2564 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -18,7 +18,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.PackageManager;
 import android.hardware.security.keymint.KeyParameter;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
@@ -300,12 +299,6 @@
             return false;
         }
 
-        private static boolean hasKeyMintV2() {
-            PackageManager pm = android.app.AppGlobals.getInitialApplication().getPackageManager();
-            return pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 200)
-                    && !pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 300);
-        }
-
         @Override
         protected final void addAlgorithmSpecificParametersToBegin(
                 @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) {
@@ -314,12 +307,11 @@
                     KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
             ));
             // Only add the KM_TAG_RSA_OAEP_MGF_DIGEST tag to begin() if the MGF Digest is
-            // present in the key properties or KeyMint version is 200. Keys generated prior to
-            // Android 14 did not have this tag (Keystore didn't add it) and hence not present in
-            // imported key as well, so specifying any MGF digest tag would cause a begin()
-            // operation (on an Android 14 device) to fail (with a key that was generated on
-            // Android 13 or below).
-            if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics) || hasKeyMintV2()) {
+            // present in the key properties. Keys generated prior to Android 14 did not have
+            // this tag (Keystore didn't add it) so specifying any MGF digest tag would cause
+            // a begin() operation (on an Android 14 device) to fail (with a key that was generated
+            // on Android 13 or below).
+            if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics)) {
                 parameters.add(KeyStore2ParameterUtils.makeEnum(
                         KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest
                 ));
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index a605e2b..8bd500e 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -166,7 +166,7 @@
         // *.kt sources are inside a filegroup.
         "kotlin-annotations",
     ],
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
     manifest: "AndroidManifest.xml",
     plugins: ["dagger2-compiler"],
 }
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
index 0ca912e..d93e9ba 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml
@@ -25,9 +25,8 @@
 
     <ImageButton
         android:id="@+id/caption_handle"
-        android:layout_width="176dp"
+        android:layout_width="128dp"
         android:layout_height="42dp"
-        android:paddingHorizontal="24dp"
         android:paddingVertical="19dp"
         android:contentDescription="@string/handle_text"
         android:src="@drawable/decor_handle_dark"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
index d5d072a..122dcbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
@@ -94,7 +94,6 @@
          * non-overlapping.
          * @return The new bounds for this content.
          */
-        @JvmDefault
         fun calculateNewBoundsOnOverlap(
             overlappingContentBounds: Rect,
             otherContentBounds: List<Rect>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 20c3bd2..f5c6a03 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -203,8 +203,10 @@
             Context context,
             @ShellMainThread Handler mainHandler,
             @ShellMainThread Choreographer mainChoreographer,
+            ShellInit shellInit,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
+            ShellController shellController,
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopModeController> desktopModeController,
@@ -214,8 +216,10 @@
                     context,
                     mainHandler,
                     mainChoreographer,
+                    shellInit,
                     taskOrganizer,
                     displayController,
+                    shellController,
                     syncQueue,
                     transitions,
                     desktopModeController,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 711df0d..c05af73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -368,7 +368,6 @@
         /**
          * Called when the active tasks change in desktop mode.
          */
-        @JvmDefault
         fun onActiveTasksChanged(displayId: Int) {}
     }
 
@@ -379,17 +378,15 @@
         /**
          * Called when the desktop starts or stops showing freeform tasks.
          */
-        @JvmDefault
         fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
 
         /**
          * Called when the desktop stashed status changes.
          */
-        @JvmDefault
         fun onStashedChanged(displayId: Int, stashed: Boolean) {}
     }
 }
 
 private fun <T> Iterable<T>.toDumpString(): String {
     return joinToString(separator = ", ", prefix = "[", postfix = "]")
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 1acf783..22929c76 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -88,7 +88,7 @@
     }
 
     /**
-     * Starts Transition of type TRANSIT_START_MOVE_TO_DESKTOP_MODE
+     * Starts Transition of type TRANSIT_START_DRAG_TO_DESKTOP_MODE
      * @param wct WindowContainerTransaction for transition
      * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
      *                              to desktop animation
@@ -98,18 +98,18 @@
             @NonNull MoveToDesktopAnimator moveToDesktopAnimator,
             Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
         mMoveToDesktopAnimator = moveToDesktopAnimator;
-        startTransition(Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE, wct,
+        startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
                 onAnimationEndCallback);
     }
 
     /**
-     * Starts Transition of type TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE
+     * Starts Transition of type TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
      * @param wct WindowContainerTransaction for transition
      * @param onAnimationEndCallback to be called after animation
      */
     public void finalizeMoveToDesktop(@NonNull WindowContainerTransaction wct,
             Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
-        startTransition(Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE, wct,
+        startTransition(Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, wct,
                 onAnimationEndCallback);
     }
 
@@ -124,7 +124,7 @@
             MoveToDesktopAnimator moveToDesktopAnimator,
             Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
         mMoveToDesktopAnimator = moveToDesktopAnimator;
-        startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct,
+        startTransition(Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE, wct,
                 onAnimationEndCallback);
     }
 
@@ -167,7 +167,7 @@
         }
 
         final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
-        if (type == Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE
+        if (type == Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             // Transitioning to freeform but keeping fullscreen bounds, so the crop is set
             // to null and we don't require an animation
@@ -194,7 +194,7 @@
         }
 
         Rect endBounds = change.getEndAbsBounds();
-        if (type == Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE
+        if (type == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
                 && !endBounds.isEmpty()) {
             // This Transition animates a task to freeform bounds after being dragged into freeform
@@ -246,7 +246,7 @@
             return true;
         }
 
-        if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+        if (type == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
             // This Transition animates a task to fullscreen after being dragged from the status
             // bar and then released back into the status bar area
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index af8ef17..7699b4b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -737,12 +737,23 @@
         Intent fillInIntent2 = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
         final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
+        final ActivityOptions activityOptions1 = options1 != null
+                ? ActivityOptions.fromBundle(options1) : ActivityOptions.makeBasic();
+        final ActivityOptions activityOptions2 = options2 != null
+                ? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
         if (samePackage(packageName1, packageName2, userId1, userId2)) {
             if (supportMultiInstancesSplit(packageName1)) {
                 fillInIntent1 = new Intent();
                 fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 fillInIntent2 = new Intent();
                 fillInIntent2.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+                if (shortcutInfo1 != null) {
+                    activityOptions1.setApplyMultipleTaskFlagForShortcut(true);
+                }
+                if (shortcutInfo2 != null) {
+                    activityOptions2.setApplyMultipleTaskFlagForShortcut(true);
+                }
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
                 pendingIntent2 = null;
@@ -754,9 +765,10 @@
                         Toast.LENGTH_SHORT).show();
             }
         }
-        mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1, options1,
-                pendingIntent2, fillInIntent2, shortcutInfo2, options2, splitPosition, splitRatio,
-                remoteTransition, instanceId);
+        mStageCoordinator.startIntents(pendingIntent1, fillInIntent1, shortcutInfo1,
+                activityOptions1.toBundle(), pendingIntent2, fillInIntent2, shortcutInfo2,
+                activityOptions2.toBundle(), splitPosition, splitRatio, remoteTransition,
+                instanceId);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index e52fd00..dc78c9b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -407,7 +407,7 @@
                             change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
                 }
                 // Rotation change of independent non display window container.
-                if (change.getParent() == null
+                if (change.getParent() == null && !change.hasFlags(FLAG_IS_DISPLAY)
                         && change.getStartRotation() != change.getEndRotation()) {
                     startRotationAnimation(startTransaction, change, info,
                             ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a242c72..c22cc6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -186,9 +186,12 @@
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
-        final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+        if (remoteTransition == null) return;
+
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "   Merge into remote: %s",
                 remoteTransition);
+
+        final IRemoteTransition remote = remoteTransition.getRemoteTransition();
         if (remote == null) return;
 
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4ca383f..e45dacf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -150,18 +150,18 @@
     public static final int TRANSIT_RESTORE_FROM_MAXIMIZE = WindowManager.TRANSIT_FIRST_CUSTOM + 9;
 
     /** Transition type for starting the move to desktop mode. */
-    public static final int TRANSIT_START_MOVE_TO_DESKTOP_MODE =
+    public static final int TRANSIT_START_DRAG_TO_DESKTOP_MODE =
             WindowManager.TRANSIT_FIRST_CUSTOM + 10;
 
     /** Transition type for finalizing the move to desktop mode. */
-    public static final int TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE =
+    public static final int TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE =
             WindowManager.TRANSIT_FIRST_CUSTOM + 11;
 
     /** Transition type to fullscreen from desktop mode. */
     public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
 
     /** Transition type to animate back to fullscreen when drag to freeform is cancelled. */
-    public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE =
+    public static final int TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE =
             WindowManager.TRANSIT_FIRST_CUSTOM + 13;
 
     /** Transition type to animate the toggle resize between the max and default desktop sizes. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 80cf96a..2d7e6a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -72,6 +72,9 @@
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
 import com.android.wm.shell.splitscreen.SplitScreenController;
+import com.android.wm.shell.sysui.KeyguardChangeListener;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.TaskCornersListener;
 
@@ -89,6 +92,7 @@
     private final DesktopModeWindowDecoration.Factory mDesktopModeWindowDecorFactory;
     private final ActivityTaskManager mActivityTaskManager;
     private final ShellTaskOrganizer mTaskOrganizer;
+    private final ShellController mShellController;
     private final Context mContext;
     private final Handler mMainHandler;
     private final Choreographer mMainChoreographer;
@@ -114,30 +118,37 @@
 
     private MoveToDesktopAnimator mMoveToDesktopAnimator;
     private final Rect mDragToDesktopAnimationStartBounds = new Rect();
+    private final DesktopModeKeyguardChangeListener mDesktopModeKeyguardChangeListener;
 
     public DesktopModeWindowDecorViewModel(
             Context context,
             Handler mainHandler,
             Choreographer mainChoreographer,
+            ShellInit shellInit,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
+            ShellController shellController,
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopModeController> desktopModeController,
-            Optional<DesktopTasksController> desktopTasksController) {
+            Optional<DesktopTasksController> desktopTasksController
+    ) {
         this(
                 context,
                 mainHandler,
                 mainChoreographer,
+                shellInit,
                 taskOrganizer,
                 displayController,
+                shellController,
                 syncQueue,
                 transitions,
                 desktopModeController,
                 desktopTasksController,
                 new DesktopModeWindowDecoration.Factory(),
                 new InputMonitorFactory(),
-                SurfaceControl.Transaction::new);
+                SurfaceControl.Transaction::new,
+                new DesktopModeKeyguardChangeListener());
     }
 
     @VisibleForTesting
@@ -145,20 +156,24 @@
             Context context,
             Handler mainHandler,
             Choreographer mainChoreographer,
+            ShellInit shellInit,
             ShellTaskOrganizer taskOrganizer,
             DisplayController displayController,
+            ShellController shellController,
             SyncTransactionQueue syncQueue,
             Transitions transitions,
             Optional<DesktopModeController> desktopModeController,
             Optional<DesktopTasksController> desktopTasksController,
             DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
             InputMonitorFactory inputMonitorFactory,
-            Supplier<SurfaceControl.Transaction> transactionFactory) {
+            Supplier<SurfaceControl.Transaction> transactionFactory,
+            DesktopModeKeyguardChangeListener desktopModeKeyguardChangeListener) {
         mContext = context;
         mMainHandler = mainHandler;
         mMainChoreographer = mainChoreographer;
         mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
         mTaskOrganizer = taskOrganizer;
+        mShellController = shellController;
         mDisplayController = displayController;
         mSyncQueue = syncQueue;
         mTransitions = transitions;
@@ -168,6 +183,13 @@
         mDesktopModeWindowDecorFactory = desktopModeWindowDecorFactory;
         mInputMonitorFactory = inputMonitorFactory;
         mTransactionFactory = transactionFactory;
+        mDesktopModeKeyguardChangeListener = desktopModeKeyguardChangeListener;
+
+        shellInit.addInitCallback(this::onInit, this);
+    }
+
+    private void onInit() {
+        mShellController.addKeyguardChangeListener(mDesktopModeKeyguardChangeListener);
     }
 
     @Override
@@ -197,8 +219,8 @@
             @NonNull TransitionInfo info,
             @NonNull TransitionInfo.Change change) {
         if (change.getMode() == WindowManager.TRANSIT_CHANGE
-                && (info.getType() == Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE
-                || info.getType() == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
+                && (info.getType() == Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE
+                || info.getType() == Transitions.TRANSIT_CANCEL_DRAG_TO_DESKTOP_MODE
                 || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE
                 || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)) {
             mWindowDecorByTaskId.get(change.getTaskInfo().taskId)
@@ -796,6 +818,10 @@
                 && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) {
             return false;
         }
+        if (mDesktopModeKeyguardChangeListener.isKeyguardVisibleAndOccluded()
+                && taskInfo.isFocused) {
+            return false;
+        }
         return DesktopModeStatus.isProto2Enabled()
                 && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED
                 && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
@@ -884,6 +910,22 @@
             mDesktopTasksController.ifPresent(d -> d.removeCornersForTask(taskId));
         }
     }
+
+    static class DesktopModeKeyguardChangeListener implements KeyguardChangeListener {
+        private boolean mIsKeyguardVisible;
+        private boolean mIsKeyguardOccluded;
+
+        @Override
+        public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
+                boolean animatingDismiss) {
+            mIsKeyguardVisible = visible;
+            mIsKeyguardOccluded = occluded;
+        }
+
+        public boolean isKeyguardVisibleAndOccluded() {
+            return mIsKeyguardVisible && mIsKeyguardOccluded;
+        }
+    }
 }
 
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
index fd56a6e..8a3c2c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
@@ -42,7 +42,7 @@
 import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
 import org.junit.Assert.assertNotNull
 
-internal object SplitScreenUtils {
+object SplitScreenUtils {
     private const val TIMEOUT_MS = 3_000L
     private const val DRAG_DURATION_MS = 1_000L
     private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 0f9579d..69c8ecd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -17,7 +17,6 @@
 package com.android.wm.shell.flicker.appcompat
 
 import android.content.Context
-import android.system.helpers.CommandsHelper
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTestData
@@ -29,15 +28,18 @@
 import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
 import com.android.wm.shell.flicker.appWindowKeepVisible
 import com.android.wm.shell.flicker.layerKeepVisible
-import org.junit.After
+
 import org.junit.Assume
 import org.junit.Before
+import org.junit.Rule
 
 abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
     protected val context: Context = instrumentation.context
     protected val letterboxApp = LetterboxAppHelper(instrumentation)
-    lateinit var cmdHelper: CommandsHelper
-    private lateinit var letterboxStyle: HashMap<String, String>
+
+    @JvmField
+    @Rule
+    val letterboxRule: LetterboxRule = LetterboxRule()
 
     /** {@inheritDoc} */
     override val transition: FlickerBuilder.() -> Unit
@@ -52,50 +54,7 @@
 
     @Before
     fun before() {
-        cmdHelper = CommandsHelper.getInstance(instrumentation)
-        Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest())
-        letterboxStyle = mapLetterboxStyle()
-        resetLetterboxStyle()
-        setLetterboxEducationEnabled(false)
-    }
-
-    @After
-    fun after() {
-        resetLetterboxStyle()
-    }
-
-    private fun mapLetterboxStyle(): HashMap<String, String> {
-        val res = cmdHelper.executeShellCommand("wm get-letterbox-style")
-        val lines = res.lines()
-        val map = HashMap<String, String>()
-        for (line in lines) {
-            val keyValuePair = line.split(":")
-            if (keyValuePair.size == 2) {
-                val key = keyValuePair[0].trim()
-                map[key] = keyValuePair[1].trim()
-            }
-        }
-        return map
-    }
-
-    private fun getLetterboxStyle(): HashMap<String, String> {
-        if (!::letterboxStyle.isInitialized) {
-            letterboxStyle = mapLetterboxStyle()
-        }
-        return letterboxStyle
-    }
-
-    private fun resetLetterboxStyle() {
-        cmdHelper.executeShellCommand("wm reset-letterbox-style")
-    }
-
-    private fun setLetterboxEducationEnabled(enabled: Boolean) {
-        cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled")
-    }
-
-    private fun isIgnoreOrientationRequest(): Boolean {
-        val res = cmdHelper.executeShellCommand("wm get-ignore-orientation-request")
-        return res != null && res.contains("true")
+        Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
     }
 
     fun FlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
@@ -115,7 +74,7 @@
 
     /** Only run on tests with config_letterboxActivityCornersRadius != 0 in devices */
     private fun assumeLetterboxRoundedCornersEnabled() {
-        Assume.assumeTrue(getLetterboxStyle().getValue("Corner radius") != "0")
+        Assume.assumeTrue(letterboxRule.hasCornerRadius)
     }
 
     fun assertLetterboxAppVisibleAtStartAndEnd() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
new file mode 100644
index 0000000..5a1136f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/LetterboxRule.kt
@@ -0,0 +1,108 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.app.Instrumentation
+import android.system.helpers.CommandsHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+/**
+ * JUnit Rule to handle letterboxStyles and states
+ */
+class LetterboxRule(
+        private val withLetterboxEducationEnabled: Boolean = false,
+        private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
+        private val cmdHelper: CommandsHelper = CommandsHelper.getInstance(instrumentation)
+) : TestRule {
+
+    private val execAdb: (String) -> String = {cmd -> cmdHelper.executeShellCommand(cmd)}
+    private lateinit var _letterboxStyle: MutableMap<String, String>
+
+    val letterboxStyle: Map<String, String>
+        get() {
+            if (!::_letterboxStyle.isInitialized) {
+                _letterboxStyle = mapLetterboxStyle()
+            }
+            return _letterboxStyle
+        }
+
+    val cornerRadius: Int?
+        get() = asInt(letterboxStyle["Corner radius"])
+
+    val hasCornerRadius: Boolean
+        get() {
+            val radius = cornerRadius
+            return radius != null && radius > 0
+        }
+
+    val isIgnoreOrientationRequest: Boolean
+        get() = execAdb("wm get-ignore-orientation-request")?.contains("true") ?: false
+
+    override fun apply(base: Statement?, description: Description?): Statement {
+        resetLetterboxStyle()
+        _letterboxStyle = mapLetterboxStyle()
+        val isLetterboxEducationEnabled = _letterboxStyle.getValue("Is education enabled")
+        var hasLetterboxEducationStateChanged = false
+        if ("$withLetterboxEducationEnabled" != isLetterboxEducationEnabled) {
+            hasLetterboxEducationStateChanged = true
+            execAdb("wm set-letterbox-style --isEducationEnabled " +
+                    withLetterboxEducationEnabled)
+        }
+        return try {
+            object : Statement() {
+                @Throws(Throwable::class)
+                override fun evaluate() {
+                    base!!.evaluate()
+                }
+            }
+        } finally {
+            if (hasLetterboxEducationStateChanged) {
+                execAdb("wm set-letterbox-style --isEducationEnabled " +
+                        isLetterboxEducationEnabled
+                )
+            }
+            resetLetterboxStyle()
+        }
+    }
+
+    private fun mapLetterboxStyle(): HashMap<String, String> {
+        val res = execAdb("wm get-letterbox-style")
+        val lines = res.lines()
+        val map = HashMap<String, String>()
+        for (line in lines) {
+            val keyValuePair = line.split(":")
+            if (keyValuePair.size == 2) {
+                val key = keyValuePair[0].trim()
+                map[key] = keyValuePair[1].trim()
+            }
+        }
+        return map
+    }
+
+    private fun resetLetterboxStyle() {
+        execAdb("wm reset-letterbox-style")
+    }
+
+    private fun asInt(str: String?): Int? = try {
+        str?.toInt()
+    } catch (e: NumberFormatException) {
+        null
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
index a7bd258..67d5718 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test launching app in size compat mode.
  *
- * To run this test: `atest WMShellFlickerTests:OpenAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsOther:OpenAppInSizeCompatModeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
new file mode 100644
index 0000000..e6ca261
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenTransparentActivityTest.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching app in size compat mode.
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:OpenTransparentActivityTest`
+ *
+ * Actions:
+ * ```
+ *     Launch a letteboxed app and then a transparent activity from it. We test the bounds
+ *     are the same.
+ * ```
+ *
+ * Notes:
+ * ```
+ *     Some default assertions (e.g., nav bar, status bar and screen covered)
+ *     are inherited [BaseTest]
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+class OpenTransparentActivityTest(flicker: LegacyFlickerTest) : TransparentBaseAppCompat(flicker) {
+
+    /** {@inheritDoc} */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            setup {
+                letterboxTranslucentLauncherApp.launchViaIntent(wmHelper)
+            }
+            transitions {
+                waitAndGetLaunchTransparent()?.click() ?: error("Launch Transparent not found")
+            }
+            teardown {
+                letterboxTranslucentApp.exit(wmHelper)
+                letterboxTranslucentLauncherApp.exit(wmHelper)
+            }
+        }
+
+    /**
+     * Checks the transparent activity is launched on top of the opaque one
+     */
+    @Postsubmit
+    @Test
+    fun translucentActivityIsLaunchedOnTopOfOpaqueActivity() {
+        flicker.assertWm {
+            this.isAppWindowOnTop(letterboxTranslucentLauncherApp)
+                .then()
+                .isAppWindowOnTop(letterboxTranslucentApp)
+        }
+    }
+
+    /**
+     * Checks that the activity is letterboxed
+     */
+    @Postsubmit
+    @Test
+    fun translucentActivityIsLetterboxed() {
+        flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) }
+    }
+
+    /**
+     * Checks that the translucent activity inherits bounds from the opaque one.
+     */
+    @Postsubmit
+    @Test
+    fun translucentActivityInheritsBoundsFromOpaqueActivity() {
+        flicker.assertLayersEnd {
+            this.visibleRegion(letterboxTranslucentApp)
+                .coversExactly(visibleRegion(letterboxTranslucentLauncherApp).region)
+        }
+    }
+
+    /**
+     * Checks that the translucent activity has rounded corners
+     */
+    @Postsubmit
+    @Test
+    fun translucentActivityHasRoundedCorners() {
+        flicker.assertLayersEnd {
+            this.hasRoundedCorners(letterboxTranslucentApp)
+        }
+    }
+
+    companion object {
+        /**
+         * Creates the test configurations.
+         *
+         * See [FlickerTestFactory.rotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<FlickerTest> {
+            return LegacyFlickerTestFactory
+                .nonRotationTests(supportedRotations = listOf(Rotation.ROTATION_90))
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
index e875aae..68fa8d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
@@ -32,7 +32,9 @@
 /**
  * Test launching a fixed portrait letterboxed app in landscape and repositioning to the right.
  *
- * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` Actions:
+ * To run this test: `atest WMShellFlickerTestsOther:RepositionFixedPortraitAppTest`
+ *
+ * Actions:
  *
  *  ```
  *      Launch a fixed portrait app in landscape to letterbox app
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
index a18a144..fcb6931a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -31,7 +31,7 @@
 /**
  * Test restarting app in size compat mode.
  *
- * To run this test: `atest WMShellFlickerTests:RestartAppInSizeCompatModeTest`
+ * To run this test: `atest WMShellFlickerTestsOther:RestartAppInSizeCompatModeTest`
  *
  * Actions:
  * ```
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
new file mode 100644
index 0000000..ea0392c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/TransparentBaseAppCompat.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.wm.shell.flicker.appcompat
+
+import android.content.Context
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.helpers.FIND_TIMEOUT
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.LetterboxAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.BaseTest
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+
+abstract class TransparentBaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+    protected val context: Context = instrumentation.context
+    protected val letterboxTranslucentLauncherApp = LetterboxAppHelper(
+        instrumentation,
+        launcherName = ActivityOptions.LaunchTransparentActivity.LABEL,
+        component = ActivityOptions.LaunchTransparentActivity.COMPONENT.toFlickerComponent()
+    )
+    protected val letterboxTranslucentApp = LetterboxAppHelper(
+        instrumentation,
+        launcherName = ActivityOptions.TransparentActivity.LABEL,
+        component = ActivityOptions.TransparentActivity.COMPONENT.toFlickerComponent()
+    )
+
+    @JvmField
+    @Rule
+    val letterboxRule: LetterboxRule = LetterboxRule()
+
+    @Before
+    fun before() {
+        Assume.assumeTrue(tapl.isTablet && letterboxRule.isIgnoreOrientationRequest)
+    }
+
+    protected fun FlickerTestData.waitAndGetLaunchTransparent(): UiObject2? =
+        device.wait(
+            Until.findObject(By.text("Launch Transparent")),
+            FIND_TIMEOUT
+        )
+
+    protected fun FlickerTestData.goBack() = device.pressBack()
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 2f7a25e..0492a18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -84,7 +85,7 @@
     }
 
     /** Checks that [pipApp] window is animated towards default position in right bottom corner */
-    @Presubmit
+    @FlakyTest(bugId = 255578530)
     @Test
     fun pipLayerMovesTowardsRightBottomCorner() {
         // in gestural nav the swipe makes PiP first go upwards
@@ -107,4 +108,10 @@
         Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
         super.focusChanges()
     }
+
+    @FlakyTest(bugId = 289943985)
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        super.visibleLayersShownMoreThanOneConsecutiveEntry()
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt
deleted file mode 100644
index 92b6227..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavPortraitBenchmark.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
-import org.junit.Test
-
-@RequiresDevice
-class CopyContentInSplitGesturalNavPortraitBenchmark : CopyContentInSplit(Rotation.ROTATION_0) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun copyContentInSplit() = super.copyContentInSplit()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt
deleted file mode 100644
index 0495079..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavLandscapeBenchmark.kt
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
-import org.junit.Test
-
-@RequiresDevice
-class DragDividerToResizeGesturalNavLandscapeBenchmark : DragDividerToResize(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun dragDividerToResize() = super.dragDividerToResize()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt
deleted file mode 100644
index aab18a6..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark.kt
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
-import org.junit.Test
-
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavLandscapeBenchmark :
-    SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun switchBetweenSplitPairs() = super.switchBetweenSplitPairs()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt
deleted file mode 100644
index 840401c..0000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.wm.shell.flicker.service.splitscreen.benchmark
-
-import android.platform.test.annotations.PlatinumTest
-import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
-import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-
-@RequiresDevice
-@RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavPortraitBenchmark : UnlockKeyguardToSplitScreen() {
-    @PlatinumTest(focusArea = "sysui")
-    @Presubmit
-    @Test
-    override fun unlockKeyguardToSplitScreen() = super.unlockKeyguardToSplitScreen()
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
similarity index 81%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
index 566adec..2494054 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
 import org.junit.Test
 
-@RequiresDevice
-class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) {
+class CopyContentInSplitGesturalNavLandscape : CopyContentInSplit(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
similarity index 81%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
index 566adec..57943ec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/CopyContentInSplitGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/CopyContentInSplitGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.CopyContentInSplit
 import org.junit.Test
 
-@RequiresDevice
-class CopyContentInSplitGesturalNavLandscapeBenchmark : CopyContentInSplit(Rotation.ROTATION_90) {
+class CopyContentInSplitGesturalNavPortrait : CopyContentInSplit(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
index e6d56b5..6f0e202 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
 import org.junit.Test
 
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavLandscapeBenchmark :
+class DismissSplitScreenByDividerGesturalNavLandscape :
     DismissSplitScreenByDivider(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
index 6752c58..dac8fa2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByDividerGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByDivider
 import org.junit.Test
 
-@RequiresDevice
-class DismissSplitScreenByDividerGesturalNavPortraitBenchmark :
+class DismissSplitScreenByDividerGesturalNavPortrait :
     DismissSplitScreenByDivider(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
index 7c9ab99..baecc16 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
 import org.junit.Test
 
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavLandscapeBenchmark :
+class DismissSplitScreenByGoHomeGesturalNavLandscape :
     DismissSplitScreenByGoHome(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
index 4b79571..3063ea5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DismissSplitScreenByGoHomeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DismissSplitScreenByGoHome
 import org.junit.Test
 
-@RequiresDevice
-class DismissSplitScreenByGoHomeGesturalNavPortraitBenchmark :
+class DismissSplitScreenByGoHomeGesturalNavPortrait :
     DismissSplitScreenByGoHome(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
similarity index 81%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
index 71ef48b..41660ba 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
 import org.junit.Test
 
-@RequiresDevice
-class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) {
+class DragDividerToResizeGesturalNavLandscape : DragDividerToResize(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
similarity index 81%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
index 71ef48b..c41ffb7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/DragDividerToResizeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/DragDividerToResizeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.DragDividerToResize
 import org.junit.Test
 
-@RequiresDevice
-class DragDividerToResizeGesturalNavPortraitBenchmark : DragDividerToResize(Rotation.ROTATION_0) {
+class DragDividerToResizeGesturalNavPortrait : DragDividerToResize(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
index c78729c..afde55b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromAllAppsGesturalNavLandscape :
     EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
index 30bce2f6..3765fc4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromAllAppsGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromAllApps
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromAllAppsGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromAllAppsGesturalNavPortrait :
     EnterSplitScreenByDragFromAllApps(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
similarity index 88%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
index b33ea7c..1e128fd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromNotificationGesturalNavLandscape :
     EnterSplitScreenByDragFromNotification(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
index 07a86a5..7767872 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromNotificationGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromNotification
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromNotificationGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromNotificationGesturalNavPortrait :
     EnterSplitScreenByDragFromNotification(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
index 9a1d127..4ca4bd1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromShortcutGesturalNavLandscape :
     EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
index 266e268..2d9d258 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromShortcutGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromShortcut
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromShortcutGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromShortcutGesturalNavPortrait :
     EnterSplitScreenByDragFromShortcut(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
index 83fc30b..c9282ac 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavLandscapeBenchmark :
+class EnterSplitScreenByDragFromTaskbarGesturalNavLandscape :
     EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
index b2f1929..68c6d6c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenByDragFromTaskbarGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenByDragFromTaskbar
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenByDragFromTaskbarGesturalNavPortraitBenchmark :
+class EnterSplitScreenByDragFromTaskbarGesturalNavPortrait :
     EnterSplitScreenByDragFromTaskbar(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
index dae92dd..304529e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavLandscapeBenchmark :
+class EnterSplitScreenFromOverviewGesturalNavLandscape :
     EnterSplitScreenFromOverview(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
index 732047b..53a6b44 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/EnterSplitScreenFromOverviewGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.EnterSplitScreenFromOverview
 import org.junit.Test
 
-@RequiresDevice
-class EnterSplitScreenFromOverviewGesturalNavPortraitBenchmark :
+class EnterSplitScreenFromOverviewGesturalNavPortrait :
     EnterSplitScreenFromOverview(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
index 1de7efd..a467830 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
 import org.junit.Test
 
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavLandscapeBenchmark :
+class SwitchAppByDoubleTapDividerGesturalNavLandscape :
     SwitchAppByDoubleTapDivider(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
index 1a046aa..1524233 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchAppByDoubleTapDividerGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchAppByDoubleTapDivider
 import org.junit.Test
 
-@RequiresDevice
-class SwitchAppByDoubleTapDividerGesturalNavPortraitBenchmark :
+class SwitchAppByDoubleTapDividerGesturalNavPortrait :
     SwitchAppByDoubleTapDivider(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
index 6e88f0e..0389659 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromAnotherAppGesturalNavLandscape :
     SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
index d26a29c..7fadf18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromAnotherAppGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromAnotherApp
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBackToSplitFromAnotherAppGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromAnotherAppGesturalNavPortrait :
     SwitchBackToSplitFromAnotherApp(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
index 4a552b0..148cc52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromHomeGesturalNavLandscape :
     SwitchBackToSplitFromHome(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
index b7376ea..0641fb0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromHomeGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromHome
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBackToSplitFromHomeGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromHomeGesturalNavPortrait :
     SwitchBackToSplitFromHome(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
index b2d05e4..741f871 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavLandscape.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavLandscapeBenchmark :
+class SwitchBackToSplitFromRecentGesturalNavLandscape :
     SwitchBackToSplitFromRecent(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
similarity index 84%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
index 6de31b1..778e2d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBackToSplitFromRecentGesturalNavPortrait.kt
@@ -14,17 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBackToSplitFromRecent
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBackToSplitFromRecentGesturalNavPortraitBenchmark :
+class SwitchBackToSplitFromRecentGesturalNavPortrait :
     SwitchBackToSplitFromRecent(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
similarity index 80%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
index b074f2c..dcdca70 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavLandscape.kt
@@ -14,18 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark :
-    SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+class SwitchBetweenSplitPairsGesturalNavLandscape : SwitchBetweenSplitPairs(Rotation.ROTATION_90) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
similarity index 80%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
index b074f2c..3c69311 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/SwitchBetweenSplitPairsGesturalNavPortraitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/SwitchBetweenSplitPairsGesturalNavPortrait.kt
@@ -14,18 +14,15 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.SwitchBetweenSplitPairs
 import org.junit.Test
 
-@RequiresDevice
-class SwitchBetweenSplitPairsGesturalNavPortraitBenchmark :
-    SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
+class SwitchBetweenSplitPairsGesturalNavPortrait : SwitchBetweenSplitPairs(Rotation.ROTATION_0) {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
similarity index 82%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
index c402aa4..c6566f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavLandscape.kt
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.BlockJUnit4ClassRunner
 
-@RequiresDevice
 @RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() {
+class UnlockKeyguardToSplitScreenGesturalNavLandscape : UnlockKeyguardToSplitScreen() {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
similarity index 82%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
index c402aa4..bb1a502 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/benchmark/UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/platinum/UnlockKeyguardToSplitScreenGesturalNavPortrait.kt
@@ -14,19 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell.flicker.service.splitscreen.benchmark
+package com.android.wm.shell.flicker.service.splitscreen.platinum
 
 import android.platform.test.annotations.PlatinumTest
 import android.platform.test.annotations.Presubmit
-import androidx.test.filters.RequiresDevice
 import com.android.wm.shell.flicker.service.splitscreen.scenarios.UnlockKeyguardToSplitScreen
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.BlockJUnit4ClassRunner
 
-@RequiresDevice
 @RunWith(BlockJUnit4ClassRunner::class)
-class UnlockKeyguardToSplitScreenGesturalNavLandscapeBenchmark : UnlockKeyguardToSplitScreen() {
+class UnlockKeyguardToSplitScreenGesturalNavPortrait : UnlockKeyguardToSplitScreen() {
     @PlatinumTest(focusArea = "sysui")
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
index d44d177..9f9d4bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -47,6 +47,7 @@
     fun setup() {
         tapl.setEnableRotation(true)
         tapl.setExpectedRotation(rotation.value)
+        tapl.workspace.switchToOverview().dismissAllTasks()
 
         SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
 
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index ad4d97f..38e9f39 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -61,7 +61,7 @@
         "libstaticjvmtiagent",
     ],
 
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 
     plugins: ["dagger2-compiler"],
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
index 885ae38..772d97d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandlerTest.java
@@ -99,7 +99,7 @@
         final int taskId = 1;
         WindowContainerTransaction wct = new WindowContainerTransaction();
         doReturn(mToken).when(mTransitions)
-                .startTransition(Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE, wct,
+                .startTransition(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE, wct,
                         mEnterDesktopTaskTransitionHandler);
         doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId();
 
@@ -108,7 +108,7 @@
 
         TransitionInfo.Change change =
                 createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
-        TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_MOVE_TO_DESKTOP_MODE,
+        TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_START_DRAG_TO_DESKTOP_MODE,
                 change);
 
 
@@ -121,7 +121,7 @@
 
     @Test
     public void testTransitEnterDesktopModeAnimation() throws Throwable {
-        final int transitionType = Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE;
+        final int transitionType = Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE;
         final int taskId = 1;
         WindowContainerTransaction wct = new WindowContainerTransaction();
         doReturn(mToken).when(mTransitions)
@@ -132,7 +132,7 @@
                 createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);
         change.setEndAbsBounds(new Rect(0, 0, 1, 1));
         TransitionInfo info = createTransitionInfo(
-                Transitions.TRANSIT_FINALIZE_MOVE_TO_DESKTOP_MODE, change);
+                Transitions.TRANSIT_FINALIZE_DRAG_TO_DESKTOP_MODE, change);
 
         runOnUiThread(() -> {
             try {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
index adc2a6f..596d6dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java
@@ -18,12 +18,15 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -53,6 +56,8 @@
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.desktopmode.DesktopModeController;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
 import org.junit.Before;
@@ -91,6 +96,10 @@
     @Mock private Supplier<SurfaceControl.Transaction> mTransactionFactory;
     @Mock private SurfaceControl.Transaction mTransaction;
     @Mock private Display mDisplay;
+    @Mock private ShellController mShellController;
+    @Mock private ShellInit mShellInit;
+    @Mock private DesktopModeWindowDecorViewModel.DesktopModeKeyguardChangeListener
+            mDesktopModeKeyguardChangeListener;
     private final List<InputManager> mMockInputManagers = new ArrayList<>();
 
     private DesktopModeWindowDecorViewModel mDesktopModeWindowDecorViewModel;
@@ -104,15 +113,18 @@
                 mContext,
                 mMainHandler,
                 mMainChoreographer,
+                mShellInit,
                 mTaskOrganizer,
                 mDisplayController,
+                mShellController,
                 mSyncQueue,
                 mTransitions,
                 Optional.of(mDesktopModeController),
                 Optional.of(mDesktopTasksController),
                 mDesktopModeWindowDecorFactory,
                 mMockInputMonitorFactory,
-                mTransactionFactory
+                mTransactionFactory,
+                mDesktopModeKeyguardChangeListener
             );
 
         doReturn(mDesktopModeWindowDecoration)
@@ -121,6 +133,7 @@
         doReturn(mTransaction).when(mTransactionFactory).get();
         doReturn(mDisplayLayout).when(mDisplayController).getDisplayLayout(anyInt());
         doReturn(STABLE_INSETS).when(mDisplayLayout).stableInsets();
+        doNothing().when(mShellController).addKeyguardChangeListener(any());
 
         when(mMockInputMonitorFactory.create(any(), any())).thenReturn(mInputMonitor);
         // InputChannel cannot be mocked because it passes to InputEventReceiver.
@@ -255,6 +268,32 @@
         verify(mInputMonitor, times(1)).dispose();
     }
 
+    @Test
+    public void testCaptionIsNotCreatedWhenKeyguardIsVisible() throws Exception {
+        doReturn(true).when(
+                mDesktopModeKeyguardChangeListener).isKeyguardVisibleAndOccluded();
+
+        final int taskId = 1;
+        final ActivityManager.RunningTaskInfo taskInfo =
+                createTaskInfo(taskId, Display.DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN);
+        taskInfo.isFocused = true;
+        SurfaceControl surfaceControl = mock(SurfaceControl.class);
+        runOnMainThread(() -> {
+            final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
+            final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
+
+            mDesktopModeWindowDecorViewModel.onTaskOpening(
+                    taskInfo, surfaceControl, startT, finishT);
+
+            taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+            taskInfo.configuration.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
+            mDesktopModeWindowDecorViewModel.onTaskChanging(
+                    taskInfo, surfaceControl, startT, finishT);
+        });
+        verify(mDesktopModeWindowDecorFactory, never())
+                .create(any(), any(), any(), any(), any(), any(), any(), any());
+    }
+
     private void runOnMainThread(Runnable r) throws Exception {
         final Handler mainHandler = new Handler(Looper.getMainLooper());
         final CountDownLatch latch = new CountDownLatch(1);
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 7ef82a7..ffc664c 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -61,18 +61,10 @@
         ADD_FAILURE() << "ClipState not a rect";                                     \
     }
 
-#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
-    TEST(test_case_name, test_name##_##pipeline) {                             \
-        RenderPipelineType oldType = Properties::getRenderPipelineType();      \
-        Properties::overrideRenderPipelineType(RenderPipelineType::pipeline);  \
-        functionCall;                                                          \
-        Properties::overrideRenderPipelineType(oldType);                       \
-    };
-
-#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
-    INNER_PIPELINE_TEST(test_case_name, test_name, pipeline,                  \
-                        TestUtils::runOnRenderThread(                         \
-                                test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name)                                \
+    TEST(test_case_name, test_name) {                                                              \
+        TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+    }
 
 /**
  * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
@@ -83,21 +75,7 @@
     public:                                                                                 \
         static void doTheThing(renderthread::RenderThread& renderThread);                   \
     };                                                                                      \
-    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);                    \
-    /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
-    /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */          \
-    void test_case_name##_##test_name##_RenderThreadTest::doTheThing(                       \
-            renderthread::RenderThread& renderThread)
-
-/**
- * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
- */
-#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name)                          \
-    class test_case_name##_##test_name##_RenderThreadTest {                                 \
-    public:                                                                                 \
-        static void doTheThing(renderthread::RenderThread& renderThread);                   \
-    };                                                                                      \
-    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL);                    \
+    INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name);                            \
     /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
     /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */          \
     void test_case_name##_##test_name##_RenderThreadTest::doTheThing(                       \
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index f3f32eb..e227999 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -14,30 +14,32 @@
  * limitations under the License.
  */
 
-#include "tests/common/LeakChecker.h"
-#include "tests/common/TestScene.h"
-
-#include "Properties.h"
-#include "hwui/Typeface.h"
-#include "HardwareBitmapUploader.h"
-#include "renderthread/RenderProxy.h"
-
+#include <android-base/parsebool.h>
 #include <benchmark/benchmark.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <fnmatch.h>
 #include <getopt.h>
 #include <pthread.h>
 #include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
+
+#include <regex>
 #include <string>
 #include <unordered_map>
 #include <vector>
 
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+#include "HardwareBitmapUploader.h"
+#include "Properties.h"
+#include "hwui/Typeface.h"
+#include "renderthread/RenderProxy.h"
+#include "tests/common/LeakChecker.h"
+#include "tests/common/TestScene.h"
 
 using namespace android;
+using namespace android::base;
 using namespace android::uirenderer;
 using namespace android::uirenderer::test;
 
@@ -69,6 +71,9 @@
   --onscreen           Render tests on device screen. By default tests
                        are offscreen rendered
   --benchmark_format   Set output format. Possible values are tabular, json, csv
+  --benchmark_list_tests Lists the tests that would run but does not run them
+  --benchmark_filter=<regex> Filters the test set to the given regex. If prefixed with `-` and test
+                       that doesn't match the given regex is run
   --renderer=TYPE      Sets the render pipeline to use. May be skiagl or skiavk
   --skip-leak-check    Skips the memory leak check
   --report-gpu-memory[=verbose]  Dumps the GPU memory usage after each test run
@@ -140,6 +145,9 @@
     if (!strcmp(format, "tabular")) {
         gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
     } else if (!strcmp(format, "json")) {
+        // We cannot print the leak check if outputing to JSON as that will break
+        // JSON parsers since it's not JSON-formatted
+        gRunLeakCheck = false;
         gBenchmarkReporter.reset(new benchmark::JSONReporter());
     } else {
         fprintf(stderr, "Unknown format '%s'\n", format);
@@ -160,6 +168,24 @@
     return true;
 }
 
+static void addTestsThatMatchFilter(std::string spec) {
+    if (spec.empty() || spec == "all") {
+        spec = ".";  // Regexp that matches all benchmarks
+    }
+    bool isNegativeFilter = false;
+    if (spec[0] == '-') {
+        spec.replace(0, 1, "");
+        isNegativeFilter = true;
+    }
+    std::regex re(spec, std::regex_constants::extended);
+    for (auto& iter : TestScene::testMap()) {
+        if ((isNegativeFilter && !std::regex_search(iter.first, re)) ||
+            (!isNegativeFilter && std::regex_search(iter.first, re))) {
+            gRunTests.push_back(iter.second);
+        }
+    }
+}
+
 // For options that only exist in long-form. Anything in the
 // 0-255 range is reserved for short options (which just use their ASCII value)
 namespace LongOpts {
@@ -170,6 +196,8 @@
     ReportFrametime,
     CpuSet,
     BenchmarkFormat,
+    BenchmarkListTests,
+    BenchmarkFilter,
     Onscreen,
     Offscreen,
     Renderer,
@@ -179,14 +207,16 @@
 }
 
 static const struct option LONG_OPTIONS[] = {
-        {"frames", required_argument, nullptr, 'f'},
-        {"repeat", required_argument, nullptr, 'r'},
+        {"count", required_argument, nullptr, 'c'},
+        {"runs", required_argument, nullptr, 'r'},
         {"help", no_argument, nullptr, 'h'},
         {"list", no_argument, nullptr, LongOpts::List},
         {"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu},
         {"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime},
         {"cpuset", required_argument, nullptr, LongOpts::CpuSet},
         {"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
+        {"benchmark_list_tests", optional_argument, nullptr, LongOpts::BenchmarkListTests},
+        {"benchmark_filter", required_argument, nullptr, LongOpts::BenchmarkFilter},
         {"onscreen", no_argument, nullptr, LongOpts::Onscreen},
         {"offscreen", no_argument, nullptr, LongOpts::Offscreen},
         {"renderer", required_argument, nullptr, LongOpts::Renderer},
@@ -197,8 +227,12 @@
 static const char* SHORT_OPTIONS = "c:r:h";
 
 void parseOptions(int argc, char* argv[]) {
+    benchmark::BenchmarkReporter::Context::executable_name = (argc > 0) ? argv[0] : "unknown";
+
     int c;
     bool error = false;
+    bool listTestsOnly = false;
+    bool testsAreFiltered = false;
     opterr = 0;
 
     while (true) {
@@ -272,6 +306,21 @@
                 }
                 break;
 
+            case LongOpts::BenchmarkListTests:
+                if (!optarg || ParseBool(optarg) == ParseBoolResult::kTrue) {
+                    listTestsOnly = true;
+                }
+                break;
+
+            case LongOpts::BenchmarkFilter:
+                if (!optarg) {
+                    error = true;
+                    break;
+                }
+                addTestsThatMatchFilter(optarg);
+                testsAreFiltered = true;
+                break;
+
             case LongOpts::Renderer:
                 if (!optarg) {
                     error = true;
@@ -346,11 +395,18 @@
                 }
             }
         } while (optind < argc);
-    } else {
+    } else if (gRunTests.empty() && !testsAreFiltered) {
         for (auto& iter : TestScene::testMap()) {
             gRunTests.push_back(iter.second);
         }
     }
+
+    if (listTestsOnly) {
+        for (auto& iter : gRunTests) {
+            std::cout << iter.name << std::endl;
+        }
+        exit(EXIT_SUCCESS);
+    }
 }
 
 int main(int argc, char* argv[]) {
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index cc7d34b..89d00d3 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -35,7 +35,7 @@
 }
 
 // TOOD(258700630): fix this test and re-enable
-RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
+RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) {
     int32_t width = DeviceInfo::get()->getWidth();
     int32_t height = DeviceInfo::get()->getHeight();
     GrDirectContext* grContext = renderThread.getGrContext();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 1e055c2..073a835 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,7 +355,7 @@
     EXPECT_EQ(3, canvas.getIndex());
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
+RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) {
     class ProjectionTestCanvas : public SkCanvas {
     public:
         ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
@@ -419,7 +419,7 @@
     EXPECT_EQ(2, canvas.getDrawCounter());
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
     /* R is backward projected on B and C is a layer.
                 A
                / \
@@ -1052,7 +1052,7 @@
 }
 
 // Verify that layers are composed with linear filtering.
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) {
     static const int CANVAS_WIDTH = 1;
     static const int CANVAS_HEIGHT = 1;
     static const int LAYER_WIDTH = 1;
@@ -1170,7 +1170,7 @@
 }
 
 // Draw a vector drawable twice but with different bounds and verify correct bounds are used.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
     static const int CANVAS_WIDTH = 100;
     static const int CANVAS_HEIGHT = 200;
     class VectorDrawableTestCanvas : public TestCanvasBase {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 9aa2e1d..0f8bd13 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -370,9 +370,9 @@
 }
 
 using namespace android::uirenderer;
-RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
     if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
-        // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+        // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants.
         GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
     }
     if (!folderExist(getExternalStorageFolder())) {
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 1a1ce1e..f6be7b2 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -136,7 +136,7 @@
     }
 };
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
@@ -195,7 +195,7 @@
     canvasContext->destroy();
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
     auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
     ContextFactory contextFactory;
     std::unique_ptr<CanvasContext> canvasContext(
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6f180e7..3ded540 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -42,7 +42,7 @@
 using namespace android::uirenderer::renderthread;
 using namespace android::uirenderer::skiapipeline;
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
     auto redNode = TestUtils::createSkiaNode(
             0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
                 redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -62,7 +62,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
     auto halfGreenNode = TestUtils::createSkiaNode(
             0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
                 Paint greenPaint;
@@ -89,7 +89,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
     auto redNode = TestUtils::createSkiaNode(
             0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
                 redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -111,7 +111,7 @@
     ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
     auto redNode = TestUtils::createSkiaNode(
             0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
                 redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -154,7 +154,7 @@
     blueNode->setLayerSurface(sk_sp<SkSurface>());
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
     ScopedProperty<bool> prop(Properties::debugOverdraw, true);
 
     auto whiteNode = TestUtils::createSkiaNode(
@@ -227,7 +227,7 @@
 };
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
     class DeferTestCanvas : public SkCanvas {
     public:
         DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -297,7 +297,7 @@
     EXPECT_EQ(4, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped) {
     static const int CANVAS_WIDTH = 200;
     static const int CANVAS_HEIGHT = 200;
     class ClippedTestCanvas : public SkCanvas {
@@ -330,7 +330,7 @@
 }
 
 // Test renderFrame with a dirty clip and a pre-transform matrix.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) {
     static const int CANVAS_WIDTH = 200;
     static const int CANVAS_HEIGHT = 100;
     static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1);
@@ -366,7 +366,7 @@
     EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
     static const int CANVAS_WIDTH = 50;
     static const int CANVAS_HEIGHT = 50;
     class ClipReplaceTestCanvas : public SkCanvas {
@@ -396,7 +396,7 @@
     EXPECT_EQ(1, surface->canvas()->mDrawCounter);
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+RENDERTHREAD_TEST(SkiaPipeline, context_lost) {
     test::TestContext context;
     auto surface = context.surface();
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
@@ -410,7 +410,7 @@
     EXPECT_TRUE(pipeline->isSurfaceReady());
 }
 
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) {
     // create a pipeline and add a picture callback
     auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
     int callbackCount = 0;
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 10c874e..76cbc8a 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -14,15 +14,15 @@
  * limitations under the License.
  */
 
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <getopt.h>
+#include <signal.h>
 
 #include "Properties.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
 #include "hwui/Typeface.h"
 #include "tests/common/LeakChecker.h"
 
-#include <signal.h>
-
 using namespace std;
 using namespace android;
 using namespace android::uirenderer;
@@ -45,6 +45,57 @@
     raise(sig);
 }
 
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+    Reserved = 255,
+    Renderer,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+        {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}};
+
+static RenderPipelineType parseRenderer(const char* renderer) {
+    // Anything that's not skiavk is skiagl
+    if (!strcmp(renderer, "skiavk")) {
+        return RenderPipelineType::SkiaVulkan;
+    }
+    return RenderPipelineType::SkiaGL;
+}
+
+struct Options {
+    RenderPipelineType renderer = RenderPipelineType::SkiaGL;
+};
+
+Options parseOptions(int argc, char* argv[]) {
+    int c;
+    opterr = 0;
+    Options opts;
+
+    while (true) {
+        /* getopt_long stores the option index here. */
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index);
+
+        if (c == -1) break;
+
+        switch (c) {
+            case 0:
+                // Option set a flag, don't need to do anything
+                // (although none of the current LONG_OPTIONS do this...)
+                break;
+
+            case LongOpts::Renderer:
+                opts.renderer = parseRenderer(optarg);
+                break;
+        }
+    }
+    return opts;
+}
+
 class TypefaceEnvironment : public testing::Environment {
 public:
     virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); }
@@ -64,8 +115,9 @@
 
     // Avoid talking to SF
     Properties::isolatedProcess = true;
-    // Default to GLES (Vulkan-aware tests will override this)
-    Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+
+    auto opts = parseOptions(argc, argv);
+    Properties::overrideRenderPipelineType(opts.renderer);
 
     // Run the tests
     testing::InitGoogleTest(&argc, argv);
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 1393660..91fa873 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -450,27 +450,27 @@
     public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
             "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
 
-    final String mId;
-    final CharSequence mName;
-    final List<String> mFeatures;
+    private final String mId;
+    private final CharSequence mName;
+    private final List<String> mFeatures;
     @Type
-    final int mType;
-    final boolean mIsSystem;
-    final Uri mIconUri;
-    final CharSequence mDescription;
+    private final int mType;
+    private final boolean mIsSystem;
+    private final Uri mIconUri;
+    private final CharSequence mDescription;
     @ConnectionState
-    final int mConnectionState;
-    final String mClientPackageName;
-    final String mPackageName;
-    final int mVolumeHandling;
-    final int mVolumeMax;
-    final int mVolume;
-    final String mAddress;
-    final Set<String> mDeduplicationIds;
-    final Bundle mExtras;
-    final String mProviderId;
-    final boolean mIsVisibilityRestricted;
-    final Set<String> mAllowedPackages;
+    private final int mConnectionState;
+    private final String mClientPackageName;
+    private final String mPackageName;
+    private final int mVolumeHandling;
+    private final int mVolumeMax;
+    private final int mVolume;
+    private final String mAddress;
+    private final Set<String> mDeduplicationIds;
+    private final Bundle mExtras;
+    private final String mProviderId;
+    private final boolean mIsVisibilityRestricted;
+    private final Set<String> mAllowedPackages;
 
     MediaRoute2Info(@NonNull Builder builder) {
         mId = builder.mId;
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index 6d6a9f8..bbe5e06 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -10,5 +10,8 @@
 
 per-file *Image* = file:/graphics/java/android/graphics/OWNERS
 
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=set noparent
+per-file ExifInterface.java,ExifInterfaceUtils.java,IMediaHTTPConnection.aidl,IMediaHTTPService.aidl,JetPlayer.java,MediaDataSource.java,MediaExtractor.java,MediaHTTPConnection.java,MediaHTTPService.java,MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+
 # Haptics team also works on Ringtone
 per-file *Ringtone* = file:/services/core/java/com/android/server/vibrator/OWNERS
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index 96894d1..e12d828 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -3,3 +3,6 @@
 
 # extra for TV related files
 per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
+
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index dea7f03..5ea98c0c 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2615,7 +2615,7 @@
             return;
         }
         auto cryptoInfo =
-                cryptoInfoObj ? NativeCryptoInfo{size} : NativeCryptoInfo{env, cryptoInfoObj};
+                cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{size};
         if (env->ExceptionCheck()) {
             // Creation of cryptoInfo failed. Let the exception bubble up.
             return;
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 28b9bc0..fe26dc3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -44,7 +44,7 @@
     platform_apis: true,
     privileged: true,
 
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 
     optimize: {
         proguard_compatibility: false,
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index 8699f59..0caf505 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -70,5 +70,5 @@
 
     manifest: "AndroidManifest.xml",
 
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 3505cfb..74f04e0 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -33,8 +33,6 @@
 
 import androidx.annotation.Nullable;
 
-import java.io.File;
-
 /**
  * Installation failed: Return status code to the caller or display failure UI to user
  */
@@ -101,14 +99,8 @@
             // Set header icon and title
             PackageUtil.AppSnippet as;
             PackageManager pm = getPackageManager();
-
-            if ("package".equals(packageURI.getScheme())) {
-                as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
-                        pm.getApplicationIcon(appInfo));
-            } else {
-                final File sourceFile = new File(packageURI.getPath());
-                as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
-            }
+            as = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+                    PackageUtil.AppSnippet.class);
 
             // Store label for dialog
             mLabel = as.label;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 7bea339..1088ace 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -16,6 +16,7 @@
 
 package com.android.packageinstaller;
 
+import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_APP_SNIPPET;
 import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID;
 
 import android.app.PendingIntent;
@@ -86,7 +87,8 @@
             // ContentResolver.SCHEME_FILE
             // STAGED_SESSION_ID extra contains an ID of a previously staged install session.
             final File sourceFile = new File(mPackageURI.getPath());
-            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+            PackageUtil.AppSnippet as = getIntent()
+                    .getParcelableExtra(EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class);
 
             mAlert.setIcon(as.icon);
             mAlert.setTitle(as.label);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index ff991d2..fbc9525 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -22,7 +22,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.View;
@@ -30,7 +29,6 @@
 
 import androidx.annotation.Nullable;
 
-import java.io.File;
 import java.util.List;
 
 /**
@@ -65,18 +63,8 @@
             ApplicationInfo appInfo =
                     intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
             mAppPackageName = appInfo.packageName;
-            Uri packageURI = intent.getData();
-
-            // Set header icon and title
-            PackageManager pm = getPackageManager();
-
-            if ("package".equals(packageURI.getScheme())) {
-                mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
-                        pm.getApplicationIcon(appInfo));
-            } else {
-                File sourceFile = new File(packageURI.getPath());
-                mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
-            }
+            mAppSnippet = intent.getParcelableExtra(PackageInstallerActivity.EXTRA_APP_SNIPPET,
+                    PackageUtil.AppSnippet.class);
 
             mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
index 2278f7c..d29765e 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstalledReceiver.java
@@ -19,6 +19,8 @@
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.util.Log;
 
@@ -61,11 +63,19 @@
                 return;
             }
 
-            // TODO: Make sure the installer information here is accurate
-            String installer =
-                    context.getPackageManager().getInstallerPackageName(packageName);
-            new PackageInstalledNotificationUtils(context, installer,
-                    packageName).postAppInstalledNotification();
+            try {
+                InstallSourceInfo installerInfo =
+                        context.getPackageManager().getInstallSourceInfo(packageName);
+                String installer = installerInfo.getInstallingPackageName();
+                if (installer == null) {
+                    Log.e(TAG, "No installer package name for: " + packageName);
+                    return;
+                }
+                new PackageInstalledNotificationUtils(context, installer,
+                        packageName).postAppInstalledNotification();
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Cannot get source info for: " + packageName);
+            }
         }
     }
 }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index bc7fa02..b66679a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -84,6 +84,7 @@
     static final String EXTRA_CALLING_ATTRIBUTION_TAG = "EXTRA_CALLING_ATTRIBUTION_TAG";
     static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
     static final String EXTRA_STAGED_SESSION_ID = "EXTRA_STAGED_SESSION_ID";
+    static final String EXTRA_APP_SNIPPET = "EXTRA_APP_SNIPPET";
     private static final String ALLOW_UNKNOWN_SOURCES_KEY =
             PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
 
@@ -708,6 +709,9 @@
         if (stagedSessionId > 0) {
             newIntent.putExtra(EXTRA_STAGED_SESSION_ID, stagedSessionId);
         }
+        if (mAppSnippet != null) {
+            newIntent.putExtra(EXTRA_APP_SNIPPET, mAppSnippet);
+        }
         newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
         if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
         startActivity(newIntent);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index c2d4f18..5880a29 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -28,8 +28,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.View;
@@ -117,7 +122,7 @@
                 icon);
     }
 
-    static final class AppSnippet {
+    static final class AppSnippet implements Parcelable {
         @NonNull public CharSequence label;
         @Nullable public Drawable icon;
         public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
@@ -125,10 +130,52 @@
             this.icon = icon;
         }
 
+        private AppSnippet(Parcel in) {
+            label = in.readString();
+            Bitmap bmp = in.readParcelable(getClass().getClassLoader(), Bitmap.class);
+            icon = new BitmapDrawable(Resources.getSystem(), bmp);
+        }
+
         @Override
         public String toString() {
             return "AppSnippet[" + label + (icon != null ? "(has" : "(no ") + " icon)]";
         }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeString(label.toString());
+            Bitmap bmp = getBitmapFromDrawable(icon);
+            dest.writeParcelable(bmp, 0);
+        }
+
+        private Bitmap getBitmapFromDrawable(Drawable drawable) {
+            // Create an empty bitmap with the dimensions of our drawable
+            final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
+                    drawable.getIntrinsicHeight(),
+                    Bitmap.Config.ARGB_8888);
+            // Associate it with a canvas. This canvas will draw the icon on the bitmap
+            final Canvas canvas = new Canvas(bmp);
+            // Draw the drawable in the canvas. The canvas will ultimately paint the drawable in the
+            // bitmap held within
+            drawable.draw(canvas);
+
+            return bmp;
+        }
+
+        public static final Parcelable.Creator<AppSnippet> CREATOR = new Parcelable.Creator<>() {
+            public AppSnippet createFromParcel(Parcel in) {
+                return new AppSnippet(in);
+            }
+
+            public AppSnippet[] newArray(int size) {
+                return new AppSnippet[size];
+            }
+        };
     }
 
     /**
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
index c75f41b..081e668 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BasePreference.kt
@@ -45,11 +45,10 @@
     BaseLayout(
         title = title,
         subTitle = {
-            if (singleLineSummary) {
-                SettingsBody(body = summary, maxLines = 1)
-            } else {
-                SettingsBody(body = summary)
-            }
+            SettingsBody(
+                body = summary.value,
+                maxLines = if (singleLineSummary) 1 else Int.MAX_VALUE,
+            )
         },
         modifier = modifier,
         icon = icon,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
index 01ba8f8..57319e7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt
@@ -24,7 +24,6 @@
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.text.style.TextOverflow
@@ -35,11 +34,6 @@
 import com.android.settingslib.spa.framework.theme.toMediumWeight
 
 @Composable
-fun SettingsTitle(title: State<String>, useMediumWeight: Boolean = false) {
-    SettingsTitle(title.value, useMediumWeight)
-}
-
-@Composable
 fun SettingsTitle(title: String, useMediumWeight: Boolean = false) {
     Text(
         text = title,
@@ -55,14 +49,6 @@
 
 @Composable
 fun SettingsBody(
-    body: State<String>,
-    maxLines: Int = Int.MAX_VALUE,
-) {
-    SettingsBody(body = body.value, maxLines = maxLines)
-}
-
-@Composable
-fun SettingsBody(
     body: String,
     maxLines: Int = Int.MAX_VALUE,
 ) {
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt
index 7e5b4f8..f9c1f94 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/TextTest.kt
@@ -20,7 +20,6 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithText
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.toState
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -31,14 +30,17 @@
     val composeTestRule = createComposeRule()
 
     @Test
-    fun testTitle() {
+    fun settingsTitle() {
         composeTestRule.setContent {
             SettingsTitle(title = "myTitleValue")
-            SettingsTitle(title = "myTitleState".toState())
+        }
+        composeTestRule.onNodeWithText("myTitleValue").assertIsDisplayed()
+    }
+
+    fun placeholderTitle() {
+        composeTestRule.setContent {
             PlaceholderTitle(title = "myTitlePlaceholder")
         }
-        composeTestRule.onNodeWithText("myTitleState").assertIsDisplayed()
-        composeTestRule.onNodeWithText("myTitleValue").assertIsDisplayed()
         composeTestRule.onNodeWithText("myTitlePlaceholder").assertIsDisplayed()
     }
 }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index a0ff216..b43210f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -27,14 +27,17 @@
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.material3.Divider
+import androidx.compose.material3.HorizontalDivider
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.semantics.semantics
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import com.android.settingslib.development.DevelopmentSettingsEnabler
 import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.ui.SettingsBody
@@ -80,11 +83,22 @@
     }
 
     @Composable
-    fun FooterAppVersion() {
+    fun FooterAppVersion(showPackageName: Boolean = rememberIsDevelopmentSettingsEnabled()) {
         if (packageInfo.versionName == null) return
-        Divider()
-        Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+        HorizontalDivider()
+        Column(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
             SettingsBody(stringResource(R.string.version_text, packageInfo.versionNameBidiWrapped))
+            if (showPackageName) {
+                SettingsBody(packageInfo.packageName)
+            }
+        }
+    }
+
+    @Composable
+    private fun rememberIsDevelopmentSettingsEnabled(): Boolean {
+        val context = LocalContext.current
+        return remember {
+            DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context)
         }
     }
 
@@ -108,5 +122,5 @@
 @Composable
 internal fun AppLabel(app: ApplicationInfo, isClonedAppPage: Boolean = false) {
     val appRepository = rememberAppRepository()
-    SettingsTitle(title = appRepository.produceLabel(app, isClonedAppPage), useMediumWeight = true)
+    SettingsTitle(appRepository.produceLabel(app, isClonedAppPage).value, useMediumWeight = true)
 }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
index e846687..155905b 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -19,26 +19,26 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
 import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreference
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
-import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
 import com.android.settingslib.spaprivileged.model.app.AppRecord
 
 @Composable
 fun <T : AppRecord> AppListItemModel<T>.AppListSwitchItem(
-    onClick: () -> Unit,
     checked: State<Boolean?>,
     changeable: State<Boolean>,
     onCheckedChange: ((newChecked: Boolean) -> Unit)?,
 ) {
-    TwoTargetSwitchPreference(
+    SwitchPreference(
         model = object : SwitchPreferenceModel {
             override val title = label
             override val summary = this@AppListSwitchItem.summary
+            override val icon = @Composable {
+                AppIcon(record.app, SettingsDimension.appIconItemSize)
+            }
             override val checked = checked
             override val changeable = changeable
             override val onCheckedChange = onCheckedChange
         },
-        icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
-        onClick = onClick,
     )
 }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
new file mode 100644
index 0000000..99d3840
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.settingslib.spaprivileged.template.app
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+
+@Composable
+fun <T : AppRecord> AppListItemModel<T>.AppListTwoTargetSwitchItem(
+    onClick: () -> Unit,
+    checked: State<Boolean?>,
+    changeable: State<Boolean>,
+    onCheckedChange: ((newChecked: Boolean) -> Unit)?,
+) {
+    TwoTargetSwitchPreference(
+        model = object : SwitchPreferenceModel {
+            override val title = label
+            override val summary = this@AppListTwoTargetSwitchItem.summary
+            override val checked = checked
+            override val changeable = changeable
+            override val onCheckedChange = onCheckedChange
+        },
+        icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
+        onClick = onClick,
+    )
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
index bb56c10..6831e56 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppInfoTest.kt
@@ -93,6 +93,7 @@
         val packageInfo = PackageInfo().apply {
             applicationInfo = APP
             versionName = VERSION_NAME
+            packageName = PACKAGE_NAME
         }
         val appInfoProvider = AppInfoProvider(packageInfo)
 
@@ -105,9 +106,45 @@
         composeTestRule.onNodeWithText("version $VERSION_NAME").assertIsDisplayed()
     }
 
+    @Test
+    fun footerAppVersion_developmentEnabled_packageNameIsDisplayed() {
+        val packageInfo = PackageInfo().apply {
+            applicationInfo = APP
+            versionName = VERSION_NAME
+            packageName = PACKAGE_NAME
+        }
+        val appInfoProvider = AppInfoProvider(packageInfo)
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                appInfoProvider.FooterAppVersion(true)
+            }
+        }
+        composeTestRule.onNodeWithText(PACKAGE_NAME).assertIsDisplayed()
+    }
+
+
+    @Test
+    fun footerAppVersion_developmentDisabled_packageNameDoesNotExist() {
+        val packageInfo = PackageInfo().apply {
+            applicationInfo = APP
+            versionName = VERSION_NAME
+            packageName = PACKAGE_NAME
+        }
+        val appInfoProvider = AppInfoProvider(packageInfo)
+
+        composeTestRule.setContent {
+            CompositionLocalProvider(LocalContext provides context) {
+                appInfoProvider.FooterAppVersion(false)
+            }
+        }
+        composeTestRule.onNodeWithText(PACKAGE_NAME).assertDoesNotExist()
+    }
+
     private companion object {
         const val LABEL = "Label"
         const val VERSION_NAME = "VersionName"
+        const val PACKAGE_NAME = "package.name"
         val APP = object : ApplicationInfo() {
             override fun loadLabel(pm: PackageManager) = LABEL
         }
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
index abdcd3b..2fd1b10 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -43,7 +43,6 @@
     fun appLabel_displayed() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(null),
                 changeable = stateOf(false),
                 onCheckedChange = {},
@@ -57,7 +56,6 @@
     fun summary_displayed() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(null),
                 changeable = stateOf(false),
                 onCheckedChange = {},
@@ -68,27 +66,9 @@
     }
 
     @Test
-    fun title_onClick() {
-        var titleClicked = false
-        composeTestRule.setContent {
-            ITEM_MODEL.AppListSwitchItem(
-                onClick = { titleClicked = true },
-                checked = stateOf(false),
-                changeable = stateOf(false),
-                onCheckedChange = {},
-            )
-        }
-
-        composeTestRule.onNodeWithText(LABEL).performClick()
-
-        assertThat(titleClicked).isTrue()
-    }
-
-    @Test
     fun switch_checkIsNull() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(null),
                 changeable = stateOf(false),
                 onCheckedChange = {},
@@ -102,7 +82,6 @@
     fun switch_checked() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(true),
                 changeable = stateOf(false),
                 onCheckedChange = {},
@@ -116,7 +95,6 @@
     fun switch_notChecked() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(false),
                 changeable = stateOf(false),
                 onCheckedChange = {},
@@ -130,7 +108,6 @@
     fun switch_changeable() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(false),
                 changeable = stateOf(true),
                 onCheckedChange = {},
@@ -144,7 +121,6 @@
     fun switch_notChangeable() {
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(false),
                 changeable = stateOf(false),
                 onCheckedChange = {},
@@ -159,7 +135,6 @@
         var switchClicked = false
         composeTestRule.setContent {
             ITEM_MODEL.AppListSwitchItem(
-                onClick = {},
                 checked = stateOf(false),
                 changeable = stateOf(true),
                 onCheckedChange = { switchClicked = true },
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
new file mode 100644
index 0000000..6e7fc8e
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.settingslib.spaprivileged.template.app
+
+import android.content.pm.ApplicationInfo
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertIsOff
+import androidx.compose.ui.test.assertIsOn
+import androidx.compose.ui.test.isToggleable
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class AppListTwoTargetSwitchItemTest {
+    @get:Rule
+    val composeTestRule = createComposeRule()
+
+    @Test
+    fun appLabel_displayed() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(null),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNodeWithText(LABEL).assertIsDisplayed()
+    }
+
+    @Test
+    fun summary_displayed() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(null),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNodeWithText(SUMMARY).assertIsDisplayed()
+    }
+
+    @Test
+    fun title_onClick() {
+        var titleClicked = false
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = { titleClicked = true },
+                checked = stateOf(false),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNodeWithText(LABEL).performClick()
+
+        assertThat(titleClicked).isTrue()
+    }
+
+    @Test
+    fun switch_checkIsNull() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(null),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNode(isToggleable()).assertDoesNotExist()
+    }
+
+    @Test
+    fun switch_checked() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(true),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNode(isToggleable()).assertIsOn()
+    }
+
+    @Test
+    fun switch_notChecked() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(false),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNode(isToggleable()).assertIsOff()
+    }
+
+    @Test
+    fun switch_changeable() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(false),
+                changeable = stateOf(true),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNode(isToggleable()).assertIsEnabled()
+    }
+
+    @Test
+    fun switch_notChangeable() {
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(false),
+                changeable = stateOf(false),
+                onCheckedChange = {},
+            )
+        }
+
+        composeTestRule.onNode(isToggleable()).assertIsNotEnabled()
+    }
+
+    @Test
+    fun switch_onClick() {
+        var switchClicked = false
+        composeTestRule.setContent {
+            ITEM_MODEL.AppListTwoTargetSwitchItem(
+                onClick = {},
+                checked = stateOf(false),
+                changeable = stateOf(true),
+                onCheckedChange = { switchClicked = true },
+            )
+        }
+
+        composeTestRule.onNode(isToggleable()).performClick()
+
+        assertThat(switchClicked).isTrue()
+    }
+
+    private companion object {
+        const val PACKAGE_NAME = "package.name"
+        const val LABEL = "Label"
+        const val SUMMARY = "Summary"
+        val APP = ApplicationInfo().apply {
+            packageName = PACKAGE_NAME
+        }
+        val ITEM_MODEL = AppListItemModel(
+            record = object : AppRecord {
+                override val app = APP
+            },
+            label = LABEL,
+            summary = stateOf(SUMMARY),
+        )
+    }
+}
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 612a928..1a938d6 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -179,7 +179,8 @@
      * Check whether tile has order.
      */
     public boolean hasOrder() {
-        return mMetaData.containsKey(META_DATA_KEY_ORDER)
+        return mMetaData != null
+                && mMetaData.containsKey(META_DATA_KEY_ORDER)
                 && mMetaData.get(META_DATA_KEY_ORDER) instanceof Integer;
     }
 
@@ -204,7 +205,7 @@
         CharSequence title = null;
         ensureMetadataNotStale(context);
         final PackageManager packageManager = context.getPackageManager();
-        if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+        if (mMetaData != null && mMetaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
             if (mMetaData.containsKey(META_DATA_PREFERENCE_TITLE_URI)) {
                 // If has as uri to provide dynamic title, skip loading here. UI will later load
                 // at tile binding time.
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_auto.xml b/packages/SettingsLib/res/drawable/ic_hotspot_auto.xml
new file mode 100644
index 0000000..ddd526a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_auto.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M240,760L240,800Q240,817 228.5,828.5Q217,840 200,840L160,840Q143,840 131.5,828.5Q120,817 120,800L120,480L204,240Q210,222 225.5,211Q241,200 260,200L700,200Q719,200 734.5,211Q750,222 756,240L840,480L840,800Q840,817 828.5,828.5Q817,840 800,840L760,840Q743,840 731.5,828.5Q720,817 720,800L720,760L240,760ZM232,400L728,400L686,280L274,280L232,400ZM200,480L200,480L200,680L200,680L200,480ZM300,640Q325,640 342.5,622.5Q360,605 360,580Q360,555 342.5,537.5Q325,520 300,520Q275,520 257.5,537.5Q240,555 240,580Q240,605 257.5,622.5Q275,640 300,640ZM660,640Q685,640 702.5,622.5Q720,605 720,580Q720,555 702.5,537.5Q685,520 660,520Q635,520 617.5,537.5Q600,555 600,580Q600,605 617.5,622.5Q635,640 660,640ZM200,680L760,680L760,480L200,480L200,680Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_laptop.xml b/packages/SettingsLib/res/drawable/ic_hotspot_laptop.xml
new file mode 100644
index 0000000..5e1b184
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_laptop.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M0,800L0,720L80,720L80,120L880,120L880,720L960,720L960,800L0,800ZM400,720L560,720L560,680L400,680L400,720ZM160,600L800,600L800,200L160,200L160,600ZM160,600L160,200L160,200L160,600L160,600Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_phone.xml b/packages/SettingsLib/res/drawable/ic_hotspot_phone.xml
new file mode 100644
index 0000000..baa793c
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_phone.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M280,920Q247,920 223.5,896.5Q200,873 200,840L200,120Q200,87 223.5,63.5Q247,40 280,40L680,40Q713,40 736.5,63.5Q760,87 760,120L760,840Q760,873 736.5,896.5Q713,920 680,920L280,920ZM280,800L280,840Q280,840 280,840Q280,840 280,840L680,840Q680,840 680,840Q680,840 680,840L680,800L280,800ZM280,720L680,720L680,240L280,240L280,720ZM280,160L680,160L680,120Q680,120 680,120Q680,120 680,120L280,120Q280,120 280,120Q280,120 280,120L280,160ZM280,160L280,120Q280,120 280,120Q280,120 280,120L280,120Q280,120 280,120Q280,120 280,120L280,160L280,160ZM280,800L280,800L280,840Q280,840 280,840Q280,840 280,840L280,840Q280,840 280,840Q280,840 280,840L280,800Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_tablet.xml b/packages/SettingsLib/res/drawable/ic_hotspot_tablet.xml
new file mode 100644
index 0000000..cf67cd9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_tablet.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M120,800Q87,800 63.5,776.5Q40,753 40,720L40,240Q40,207 63.5,183.5Q87,160 120,160L840,160Q873,160 896.5,183.5Q920,207 920,240L920,720Q920,753 896.5,776.5Q873,800 840,800L120,800ZM160,240L120,240Q120,240 120,240Q120,240 120,240L120,720Q120,720 120,720Q120,720 120,720L160,720L160,240ZM240,720L720,720L720,240L240,240L240,720ZM800,240L800,720L840,720Q840,720 840,720Q840,720 840,720L840,240Q840,240 840,240Q840,240 840,240L800,240ZM800,240L840,240Q840,240 840,240Q840,240 840,240L840,240Q840,240 840,240Q840,240 840,240L800,240L800,240ZM160,240L160,240L120,240Q120,240 120,240Q120,240 120,240L120,240Q120,240 120,240Q120,240 120,240L160,240Z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_hotspot_watch.xml b/packages/SettingsLib/res/drawable/ic_hotspot_watch.xml
new file mode 100644
index 0000000..252a0db
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_hotspot_watch.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960"
+    android:tint="?attr/colorControlNormal">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M360,880L306,698Q258,660 229,603Q200,546 200,480Q200,414 229,357Q258,300 306,262L360,80L600,80L654,262Q702,300 731,357Q760,414 760,480Q760,546 731,603Q702,660 654,698L600,880L360,880ZM480,680Q563,680 621.5,621.5Q680,563 680,480Q680,397 621.5,338.5Q563,280 480,280Q397,280 338.5,338.5Q280,397 280,480Q280,563 338.5,621.5Q397,680 480,680ZM404,210Q424,205 442.5,202Q461,199 480,199Q499,199 517.5,202Q536,205 556,210L540,160L420,160L404,210ZM420,800L540,800L556,750Q536,755 517.5,757.5Q499,760 480,760Q461,760 442.5,757.5Q424,755 404,750L420,800ZM404,160L420,160L540,160L556,160Q536,160 517.5,160Q499,160 480,160Q461,160 442.5,160Q424,160 404,160ZM420,800L404,800Q424,800 442.5,800Q461,800 480,800Q499,800 517.5,800Q536,800 556,800L540,800L420,800Z"/>
+</vector>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index f522fd1..2118d2c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -356,11 +356,7 @@
         connectDevice();
     }
 
-    public HearingAidInfo getHearingAidInfo() {
-        return mHearingAidInfo;
-    }
-
-    public void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
+    void setHearingAidInfo(HearingAidInfo hearingAidInfo) {
         mHearingAidInfo = hearingAidInfo;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 0c1b793..441d3a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothCsipSetCoordinator;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.ScanFilter;
 import android.content.Context;
 import android.util.Log;
 
@@ -114,10 +115,21 @@
     /**
      * Create and return a new {@link CachedBluetoothDevice}. This assumes
      * that {@link #findDevice} has already been called and returned null.
-     * @param device the address of the new Bluetooth device
+     * @param device the new Bluetooth device
      * @return the newly created CachedBluetoothDevice object
      */
     public CachedBluetoothDevice addDevice(BluetoothDevice device) {
+        return addDevice(device, /*leScanFilters=*/null);
+    }
+
+    /**
+     * Create and return a new {@link CachedBluetoothDevice}. This assumes
+     * that {@link #findDevice} has already been called and returned null.
+     * @param device the new Bluetooth device
+     * @param leScanFilters the BLE scan filters which the device matched
+     * @return the newly created CachedBluetoothDevice object
+     */
+    public CachedBluetoothDevice addDevice(BluetoothDevice device, List<ScanFilter> leScanFilters) {
         CachedBluetoothDevice newDevice;
         final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
         synchronized (this) {
@@ -125,7 +137,7 @@
             if (newDevice == null) {
                 newDevice = new CachedBluetoothDevice(mContext, profileManager, device);
                 mCsipDeviceManager.initCsipDeviceIfNeeded(newDevice);
-                mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
+                mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice, leScanFilters);
                 if (!mCsipDeviceManager.setMemberDeviceIfNeeded(newDevice)
                         && !mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
                     mCachedDevices.add(newDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index e5e5782..efba953 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -18,10 +18,13 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.media.AudioDeviceAttributes;
 import android.media.audiopolicy.AudioProductStrategy;
+import android.os.ParcelUuid;
 import android.provider.Settings;
 import android.util.Log;
 
@@ -59,7 +62,8 @@
         mRoutingHelper = routingHelper;
     }
 
-    void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice) {
+    void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice,
+            List<ScanFilter> leScanFilters) {
         long hiSyncId = getHiSyncId(newDevice.getDevice());
         if (isValidHiSyncId(hiSyncId)) {
             // Once hiSyncId is valid, assign hearing aid info
@@ -68,6 +72,21 @@
                     .setAshaDeviceMode(getDeviceMode(newDevice.getDevice()))
                     .setHiSyncId(hiSyncId);
             newDevice.setHearingAidInfo(infoBuilder.build());
+        } else if (leScanFilters != null && !newDevice.isHearingAidDevice()) {
+            // If the device is added with hearing aid scan filter during pairing, set an empty
+            // hearing aid info to indicate it's a hearing aid device. The info will be updated
+            // when corresponding profiles connected.
+            for (ScanFilter leScanFilter: leScanFilters) {
+                final ParcelUuid serviceUuid = leScanFilter.getServiceUuid();
+                final ParcelUuid serviceDataUuid = leScanFilter.getServiceDataUuid();
+                if (BluetoothUuid.HEARING_AID.equals(serviceUuid)
+                        || BluetoothUuid.HAS.equals(serviceUuid)
+                        || BluetoothUuid.HEARING_AID.equals(serviceDataUuid)
+                        || BluetoothUuid.HAS.equals(serviceDataUuid)) {
+                    newDevice.setHearingAidInfo(new HearingAidInfo.Builder().build());
+                    break;
+                }
+            }
         }
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
index 1d433e7..5bc27195 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -59,7 +59,7 @@
 
     @Override
     public boolean isAvailable() {
-        return true;
+        return mWifiManager != null;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index f911d35..a617bf3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -28,6 +28,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
@@ -35,6 +36,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -114,6 +116,26 @@
     private static final int SCREENSAVER_HOME_CONTROLS_ENABLED_DEFAULT = 1;
     private static final int LOCKSCREEN_SHOW_CONTROLS_DEFAULT = 0;
 
+    private static final int DS_TYPE_ENABLED = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_ENABLED;
+    private static final int DS_TYPE_WHEN_TO_DREAM = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_WHEN_TO_DREAM;
+    private static final int DS_TYPE_DREAM_COMPONENT = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_DREAM_COMPONENT;
+    private static final int DS_TYPE_SHOW_ADDITIONAL_INFO = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_SHOW_ADDITIONAL_INFO;
+    private static final int DS_TYPE_SHOW_HOME_CONTROLS = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__DREAM_SETTING_TYPE__DREAM_SETTING_TYPE_SHOW_HOME_CONTROLS;
+
+    private static final int WHEN_TO_DREAM_UNSPECIFIED = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_UNSPECIFIED;
+    private static final int WHEN_TO_DREAM_CHARGING = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_WHILE_CHARGING_ONLY;
+    private static final int WHEN_TO_DREAM_DOCKED = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_WHILE_DOCKED_ONLY;
+    private static final int WHEN_TO_DREAM_CHARGING_OR_DOCKED = FrameworkStatsLog
+            .DREAM_SETTING_CHANGED__WHEN_TO_DREAM__WHEN_TO_DREAM_EITHER_CHARGING_OR_DOCKED;
+
     private final Context mContext;
     private final IDreamManager mDreamManager;
     private final DreamInfoComparator mComparator;
@@ -121,6 +143,7 @@
     private final boolean mDreamsActivatedOnSleepByDefault;
     private final boolean mDreamsActivatedOnDockByDefault;
     private final Set<ComponentName> mDisabledDreams;
+    private final List<String> mLoggableDreamPrefixes;
     private Set<Integer> mSupportedComplications;
     private static DreamBackend sInstance;
 
@@ -148,6 +171,8 @@
                         com.android.internal.R.array.config_disabledDreamComponents))
                 .map(ComponentName::unflattenFromString)
                 .collect(Collectors.toSet());
+        mLoggableDreamPrefixes = Arrays.stream(resources.getStringArray(
+                com.android.internal.R.array.config_loggable_dream_prefixes)).toList();
 
         mSupportedComplications = Arrays.stream(resources.getIntArray(
                         com.android.internal.R.array.config_supportedDreamComplications))
@@ -282,6 +307,8 @@
             default:
                 break;
         }
+
+        logDreamSettingChangeToStatsd(DS_TYPE_WHEN_TO_DREAM);
     }
 
     /** Gets all complications which have been enabled by the user. */
@@ -304,12 +331,14 @@
     public void setComplicationsEnabled(boolean enabled) {
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED, enabled ? 1 : 0);
+        logDreamSettingChangeToStatsd(DS_TYPE_SHOW_ADDITIONAL_INFO);
     }
 
     /** Sets whether home controls are enabled by the user on the dream */
     public void setHomeControlsEnabled(boolean enabled) {
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED, enabled ? 1 : 0);
+        logDreamSettingChangeToStatsd(DS_TYPE_SHOW_HOME_CONTROLS);
     }
 
     /** Gets whether home controls button is enabled on the dream */
@@ -353,6 +382,7 @@
     public void setEnabled(boolean value) {
         logd("setEnabled(%s)", value);
         setBoolean(Settings.Secure.SCREENSAVER_ENABLED, value);
+        logDreamSettingChangeToStatsd(DS_TYPE_ENABLED);
     }
 
     public boolean isActivatedOnDock() {
@@ -391,6 +421,7 @@
         try {
             ComponentName[] dreams = {dream};
             mDreamManager.setDreamComponents(dream == null ? null : dreams);
+            logDreamSettingChangeToStatsd(DS_TYPE_DREAM_COMPONENT);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to set active dream to " + dream, e);
         }
@@ -461,6 +492,68 @@
         }
     }
 
+    private void logDreamSettingChangeToStatsd(int dreamSettingType) {
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.DREAM_SETTING_CHANGED, /*atom_tag*/
+                UserHandle.myUserId(), /*uid*/
+                isEnabled(), /*enabled*/
+                getActiveDreamComponentForStatsd(), /*dream_component*/
+                getWhenToDreamForStatsd(), /*when_to_dream*/
+                getComplicationsEnabled(), /*show_additional_info*/
+                getHomeControlsEnabled(), /*show_home_controls*/
+                dreamSettingType /*dream_setting_type*/
+        );
+    }
+
+    /**
+     * Returns the user selected dream component in string format for stats logging. If the dream
+     * component is not loggable, returns "other".
+     */
+    private String getActiveDreamComponentForStatsd() {
+        final ComponentName activeDream = getActiveDream();
+        if (activeDream == null) {
+            return "";
+        }
+
+        final String component = activeDream.flattenToShortString();
+        if (isLoggableDreamComponentForStatsd(component)) {
+            return component;
+        } else {
+            return "other";
+        }
+    }
+
+    /**
+     * Whether the dream component is loggable. Only components from the predefined packages are
+     * allowed to be logged for privacy.
+     */
+    private boolean isLoggableDreamComponentForStatsd(String component) {
+        for (int i = 0; i < mLoggableDreamPrefixes.size(); i++) {
+            if (component.startsWith(mLoggableDreamPrefixes.get(i))) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the enum of "when to dream" setting for statsd logging.
+     */
+    private int getWhenToDreamForStatsd() {
+        switch (getWhenToDreamSetting()) {
+            case WHILE_CHARGING:
+                return WHEN_TO_DREAM_CHARGING;
+            case WHILE_DOCKED:
+                return WHEN_TO_DREAM_DOCKED;
+            case EITHER:
+                return WHEN_TO_DREAM_CHARGING_OR_DOCKED;
+            case NEVER:
+            default:
+                return WHEN_TO_DREAM_UNSPECIFIED;
+        }
+    }
+
     private static class DreamInfoComparator implements Comparator<DreamInfo> {
         private final ComponentName mDefaultDream;
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 7a48838..7e27560 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -154,15 +154,17 @@
     protected abstract RouteListingPreference getRouteListingPreference();
 
     /**
-     * Returns the list of currently active {@link RoutingSessionInfo routing sessions} known to the
-     * system.
+     * Returns the list of remote {@link RoutingSessionInfo routing sessions} known to the system.
      */
     @NonNull
-    protected abstract List<RoutingSessionInfo> getActiveRoutingSessions();
+    protected abstract List<RoutingSessionInfo> getRemoteSessions();
 
     @NonNull
     protected abstract List<RoutingSessionInfo> getRoutingSessionsForPackage();
 
+    @Nullable
+    protected abstract RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId);
+
     @NonNull
     protected abstract List<MediaRoute2Info> getAllRoutes();
 
@@ -496,16 +498,6 @@
         return info.getName();
     }
 
-    boolean shouldDisableMediaOutput(String packageName) {
-        if (TextUtils.isEmpty(packageName)) {
-            Log.w(TAG, "shouldDisableMediaOutput() package name is null or empty!");
-            return true;
-        }
-
-        // Disable when there is no transferable route
-        return getTransferableRoutes(packageName).isEmpty();
-    }
-
     @TargetApi(Build.VERSION_CODES.R)
     boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
         return sessionInfo.isSystemSession() // System sessions are not remote
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 8479df1..4fb0487 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -388,14 +388,12 @@
      * @param volume the value of volume
      */
     public void adjustSessionVolume(String sessionId, int volume) {
-        final List<RoutingSessionInfo> infos = getActiveMediaSession();
-        for (RoutingSessionInfo info : infos) {
-            if (TextUtils.equals(sessionId, info.getId())) {
-                mInfoMediaManager.adjustSessionVolume(info, volume);
-                return;
-            }
+        RoutingSessionInfo session = mInfoMediaManager.getRoutingSessionById(sessionId);
+        if (session != null) {
+            mInfoMediaManager.adjustSessionVolume(session, volume);
+        } else {
+            Log.w(TAG, "adjustSessionVolume: Unable to find session: " + sessionId);
         }
-        Log.w(TAG, "adjustSessionVolume: Unable to find session: " + sessionId);
     }
 
     /**
@@ -435,12 +433,12 @@
     }
 
     /**
-     * Gets the current active session.
+     * Gets the list of remote {@link RoutingSessionInfo routing sessions} known to the system.
      *
-     * @return current active session list{@link android.media.RoutingSessionInfo}
+     * <p>This list does not include any system routing sessions.
      */
-    public List<RoutingSessionInfo> getActiveMediaSession() {
-        return mInfoMediaManager.getActiveRoutingSessions();
+    public List<RoutingSessionInfo> getRemoteRoutingSessions() {
+        return mInfoMediaManager.getRemoteSessions();
     }
 
     /**
@@ -453,13 +451,6 @@
     }
 
     /**
-     * Returns {@code true} if needed to disable media output, otherwise returns {@code false}.
-     */
-    public boolean shouldDisableMediaOutput(String packageName) {
-        return mInfoMediaManager.shouldDisableMediaOutput(packageName);
-    }
-
-    /**
      * Returns {@code true} if needed to enable volume seekbar, otherwise returns {@code false}.
      */
     public boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index b7ac1dce..0be2e0e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -30,7 +30,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -151,11 +150,22 @@
 
     @Override
     @NonNull
-    protected List<RoutingSessionInfo> getActiveRoutingSessions() {
-        List<RoutingSessionInfo> infos = new ArrayList<>();
-        infos.add(mRouterManager.getSystemRoutingSession(null));
-        infos.addAll(mRouterManager.getRemoteSessions());
-        return infos;
+    protected List<RoutingSessionInfo> getRemoteSessions() {
+        return mRouterManager.getRemoteSessions();
+    }
+
+    @Nullable
+    @Override
+    protected RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId) {
+        for (RoutingSessionInfo sessionInfo : getRemoteSessions()) {
+            if (TextUtils.equals(sessionInfo.getId(), sessionId)) {
+                return sessionInfo;
+            }
+        }
+
+        RoutingSessionInfo systemSession = mRouterManager.getSystemRoutingSession(null);
+
+        return TextUtils.equals(systemSession.getId(), sessionId) ? systemSession : null;
     }
 
     @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index afab046..b9a4647 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -27,6 +27,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
 import android.net.wifi.WifiInfo;
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.util.Log;
@@ -331,6 +332,22 @@
     }
 
     /**
+     * Returns the Hotspot network icon resource.
+     *
+     * @param deviceType The device type of Hotspot network
+     */
+    public static int getHotspotIconResource(int deviceType) {
+        return switch (deviceType) {
+            case NetworkProviderInfo.DEVICE_TYPE_PHONE -> R.drawable.ic_hotspot_phone;
+            case NetworkProviderInfo.DEVICE_TYPE_TABLET -> R.drawable.ic_hotspot_tablet;
+            case NetworkProviderInfo.DEVICE_TYPE_LAPTOP -> R.drawable.ic_hotspot_laptop;
+            case NetworkProviderInfo.DEVICE_TYPE_WATCH -> R.drawable.ic_hotspot_watch;
+            case NetworkProviderInfo.DEVICE_TYPE_AUTO -> R.drawable.ic_hotspot_auto;
+            default -> R.drawable.ic_hotspot_phone;  // Return phone icon as default.
+        };
+    }
+
+    /**
      * Wrapper the {@link #getInternetIconResource} for testing compatibility.
      */
     public static class InternetIconInjector {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 0d5de88..ea10944 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -33,6 +33,8 @@
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.le.ScanFilter;
 import android.content.Context;
 import android.media.AudioAttributes;
 import android.media.AudioDeviceAttributes;
@@ -147,7 +149,7 @@
                 HearingAidProfile.DeviceSide.SIDE_RIGHT);
 
         assertThat(mCachedDevice1.getHiSyncId()).isNotEqualTo(HISYNCID1);
-        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
 
         assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
         assertThat(mCachedDevice1.getDeviceMode()).isEqualTo(
@@ -164,12 +166,43 @@
         when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
                 BluetoothHearingAid.HI_SYNC_ID_INVALID);
 
-        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, null);
 
         verify(mCachedDevice1, never()).setHearingAidInfo(any(HearingAidInfo.class));
     }
 
     /**
+     * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId and hearing aid scan filter, set an
+     * empty hearing aid info on the device.
+     */
+    @Test
+    public void initHearingAidDeviceIfNeeded_hearingAidScanFilter_setHearingAidInfo() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+                BluetoothHearingAid.HI_SYNC_ID_INVALID);
+        final ScanFilter scanFilter = new ScanFilter.Builder()
+                .setServiceData(BluetoothUuid.HEARING_AID, new byte[]{0}).build();
+
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
+
+        assertThat(mCachedDevice1.isHearingAidDevice()).isTrue();
+    }
+
+    /**
+     * Test initHearingAidDeviceIfNeeded, an invalid HiSyncId and random scan filter, not to set
+     * hearing aid info on the device.
+     */
+    @Test
+    public void initHearingAidDeviceIfNeeded_randomScanFilter_setHearingAidInfo() {
+        when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
+                BluetoothHearingAid.HI_SYNC_ID_INVALID);
+        final ScanFilter scanFilter = new ScanFilter.Builder().build();
+
+        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1, List.of(scanFilter));
+
+        assertThat(mCachedDevice1.isHearingAidDevice()).isFalse();
+    }
+
+    /**
      * Test setSubDeviceIfNeeded, a device with same HiSyncId will be set as sub device
      */
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 2edf403..00ae96c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -75,6 +75,9 @@
         when(res.getStringArray(
                 com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
                 new String[]{});
+        when(res.getStringArray(
+                com.android.internal.R.array.config_loggable_dream_prefixes)).thenReturn(
+                new String[]{});
         mBackend = new DreamBackend(mContext);
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 45b5de4..2252b69 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -832,19 +832,12 @@
     }
 
     @Test
-    public void getActiveMediaSession_returnActiveSession() {
-        RoutingSessionInfo sysSessionInfo = mock(RoutingSessionInfo.class);
+    public void getRemoteSessions_returnsRemoteSessions() {
         final List<RoutingSessionInfo> infos = new ArrayList<>();
         infos.add(mock(RoutingSessionInfo.class));
-        final List<RoutingSessionInfo> activeSessionInfos = new ArrayList<>();
-        activeSessionInfos.add(sysSessionInfo);
-        activeSessionInfos.addAll(infos);
-
-        mShadowRouter2Manager.setSystemRoutingSession(sysSessionInfo);
         mShadowRouter2Manager.setRemoteSessions(infos);
 
-        assertThat(mInfoMediaManager.getActiveRoutingSessions())
-                .containsExactlyElementsIn(activeSessionInfos);
+        assertThat(mInfoMediaManager.getRemoteSessions()).containsExactlyElementsIn(infos);
     }
 
     @Test
@@ -1083,51 +1076,4 @@
         assertThat(device.getState()).isEqualTo(STATE_SELECTED);
         assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(device);
     }
-
-    @Test
-    public void shouldDisableMediaOutput_infosIsEmpty_returnsTrue() {
-        mShadowRouter2Manager.setTransferableRoutes(new ArrayList<>());
-
-        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isTrue();
-    }
-
-    @Test
-    public void shouldDisableMediaOutput_infosSizeEqual1_returnsFalse() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final List<MediaRoute2Info> infos = new ArrayList<>();
-        infos.add(info);
-        mShadowRouter2Manager.setTransferableRoutes(infos);
-
-        when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-
-        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
-    }
-
-    @Test
-    public void shouldDisableMediaOutput_infosSizeEqual1AndNotCastDevice_returnsFalse() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final List<MediaRoute2Info> infos = new ArrayList<>();
-        infos.add(info);
-        mShadowRouter2Manager.setTransferableRoutes(infos);
-
-        when(info.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
-
-        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
-    }
-
-
-    @Test
-    public void shouldDisableMediaOutput_infosSizeOverThan1_returnsFalse() {
-        final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final MediaRoute2Info info2 = mock(MediaRoute2Info.class);
-        final List<MediaRoute2Info> infos = new ArrayList<>();
-        infos.add(info);
-        infos.add(info2);
-        mShadowRouter2Manager.setTransferableRoutes(infos);
-
-        when(info.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-        when(info2.getType()).thenReturn(TYPE_REMOTE_SPEAKER);
-
-        assertThat(mInfoMediaManager.shouldDisableMediaOutput("test")).isFalse();
-    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index d6c33ff..926b41a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -433,9 +433,9 @@
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
         when(info.getId()).thenReturn(TEST_SESSION_ID);
         routingSessionInfos.add(info);
-        when(mInfoMediaManager.getActiveRoutingSessions()).thenReturn(routingSessionInfos);
+        when(mInfoMediaManager.getRemoteSessions()).thenReturn(routingSessionInfos);
 
-        assertThat(mLocalMediaManager.getActiveMediaSession().get(0).getId())
+        assertThat(mLocalMediaManager.getRemoteRoutingSessions().get(0).getId())
                 .matches(TEST_SESSION_ID);
     }
 
@@ -544,7 +544,7 @@
         final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
         when(info.getId()).thenReturn(TEST_SESSION_ID);
         routingSessionInfos.add(info);
-        when(mInfoMediaManager.getActiveRoutingSessions()).thenReturn(routingSessionInfos);
+        when(mInfoMediaManager.getRoutingSessionById(TEST_SESSION_ID)).thenReturn(info);
 
         mLocalMediaManager.adjustSessionVolume(TEST_SESSION_ID, 10);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index b60dc6a..5293011 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -35,6 +35,7 @@
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
+import android.net.wifi.sharedconnectivity.app.NetworkProviderInfo;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.SystemClock;
@@ -201,6 +202,25 @@
     }
 
     @Test
+    public void getHotspotIconResource_deviceTypeUnknown_shouldNotCrash() {
+        WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_UNKNOWN);
+    }
+
+    @Test
+    public void getHotspotIconResource_deviceTypeExists_shouldNotNull() {
+        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_PHONE))
+                .isNotNull();
+        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_TABLET))
+                .isNotNull();
+        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_LAPTOP))
+                .isNotNull();
+        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_WATCH))
+                .isNotNull();
+        assertThat(WifiUtils.getHotspotIconResource(NetworkProviderInfo.DEVICE_TYPE_AUTO))
+                .isNotNull();
+    }
+
+    @Test
     public void testInternetIconInjector_getIcon_returnsCorrectValues() {
         WifiUtils.InternetIconInjector iconInjector = new WifiUtils.InternetIconInjector(mContext);
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7d8b066..368115b 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,6 +320,7 @@
 
 
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+    <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
     <uses-permission android:name="android.permission.SUSPEND_APPS" />
     <uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
     <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7be6043..0f16d93 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -203,7 +203,7 @@
     manifest: "AndroidManifest.xml",
 
     javacflags: ["-Adagger.fastInit=enabled"],
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 
     plugins: ["dagger2-compiler"],
 
@@ -394,7 +394,7 @@
         "android.test.base",
         "android.test.mock",
     ],
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
     aaptflags: [
         "--extra-packages",
         "com.android.systemui",
@@ -516,7 +516,7 @@
     certificate: "platform",
     privileged: true,
 
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 
     dxflags: ["--multi-dex"],
     optimize: {
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4fd4723..58c9f77 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -549,26 +549,6 @@
         </activity>
 
         <!-- started from UsbDeviceSettingsManager -->
-        <activity android:name=".usb.tv.TvUsbConfirmActivity"
-                  android:exported="true"
-                  android:launchMode="singleTop"
-                  android:permission="android.permission.MANAGE_USB"
-                  android:theme="@style/BottomSheet"
-                  android:finishOnCloseSystemDialogs="true"
-                  android:excludeFromRecents="true">
-        </activity>
-
-        <!-- started from UsbDeviceSettingsManager -->
-        <activity android:name=".usb.tv.TvUsbPermissionActivity"
-                  android:exported="true"
-                  android:launchMode="singleTop"
-                  android:permission="android.permission.MANAGE_USB"
-                  android:theme="@style/BottomSheet"
-                  android:finishOnCloseSystemDialogs="true"
-                  android:excludeFromRecents="true">
-        </activity>
-
-        <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbResolverActivity"
             android:exported="true"
             android:permission="android.permission.MANAGE_USB"
@@ -577,16 +557,6 @@
             android:excludeFromRecents="true">
         </activity>
 
-        <!-- started from HdmiCecLocalDevicePlayback -->
-        <activity android:name=".hdmi.HdmiCecSetMenuLanguageActivity"
-                  android:exported="true"
-                  android:launchMode="singleTop"
-                  android:permission="android.permission.CHANGE_CONFIGURATION"
-                  android:theme="@style/BottomSheet"
-                  android:finishOnCloseSystemDialogs="true"
-                  android:excludeFromRecents="true">
-        </activity>
-
         <!-- started from SensoryPrivacyService -->
         <activity android:name=".sensorprivacy.SensorUseStartedActivity"
                   android:exported="true"
@@ -597,27 +567,6 @@
                   android:showForAllUsers="true">
         </activity>
 
-        <!-- started from SensoryPrivacyService -->
-        <activity android:name=".sensorprivacy.television.TvUnblockSensorActivity"
-                  android:exported="true"
-                  android:launchMode="singleTop"
-                  android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
-                  android:theme="@style/BottomSheet"
-                  android:finishOnCloseSystemDialogs="true"
-                  android:showForAllUsers="true">
-        </activity>
-
-        <!-- started from SensoryPrivacyService -->
-        <activity android:name=".sensorprivacy.television.TvSensorPrivacyChangedActivity"
-            android:exported="true"
-            android:launchMode="singleTop"
-            android:permission="android.permission.MANAGE_SENSOR_PRIVACY"
-            android:theme="@style/BottomSheet"
-            android:finishOnCloseSystemDialogs="true"
-            android:showForAllUsers="true">
-        </activity>
-
-
         <!-- started from UsbDeviceSettingsManager -->
         <activity android:name=".usb.UsbAccessoryUriActivity"
             android:exported="true"
@@ -703,14 +652,6 @@
             android:exported="false"
             android:permission="android.permission.MANAGE_MEDIA_PROJECTION"/>
 
-        <!-- started from TvNotificationPanel -->
-        <activity
-            android:name=".statusbar.tv.notifications.TvNotificationPanelActivity"
-            android:excludeFromRecents="true"
-            android:launchMode="singleTask"
-            android:noHistory="true"
-            android:theme="@style/TvSidePanelTheme" />
-
         <!-- started from SliceProvider -->
         <activity android:name=".SlicePermissionActivity"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 764a855..8306620 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -302,10 +302,9 @@
 
     interface Callback {
         /** Whether we are currently on the keyguard or not. */
-        @JvmDefault fun isOnKeyguard(): Boolean = false
+        fun isOnKeyguard(): Boolean = false
 
         /** Hide the keyguard and animate using [runner]. */
-        @JvmDefault
         fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
             throw UnsupportedOperationException()
         }
@@ -316,16 +315,16 @@
 
     interface Listener {
         /** Called when an activity launch animation started. */
-        @JvmDefault fun onLaunchAnimationStart() {}
+        fun onLaunchAnimationStart() {}
 
         /**
          * Called when an activity launch animation is finished. This will be called if and only if
          * [onLaunchAnimationStart] was called earlier.
          */
-        @JvmDefault fun onLaunchAnimationEnd() {}
+        fun onLaunchAnimationEnd() {}
 
         /** Called when an activity launch animation made progress. */
-        @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {}
+        fun onLaunchAnimationProgress(linearProgress: Float) {}
     }
 
     /**
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 6119e96..06d94ac 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -44,5 +44,5 @@
         "androidx.compose.ui_ui-test-manifest",
     ],
 
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/packages/SystemUI/compose/features/tests/Android.bp
index ff534bd..c7c9140 100644
--- a/packages/SystemUI/compose/features/tests/Android.bp
+++ b/packages/SystemUI/compose/features/tests/Android.bp
@@ -44,5 +44,5 @@
         "androidx.compose.ui_ui-test-manifest",
     ],
 
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index dc450bb..fc37b355 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -48,5 +48,5 @@
     ],
     min_sdk_version: "current",
     plugins: ["dagger2-compiler"],
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 18753fd..006fc09 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -422,9 +422,6 @@
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
 -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -696,7 +693,6 @@
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
 -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
deleted file mode 100644
index fc3b4ed..0000000
--- a/packages/SystemUI/res/anim/tv_bottom_sheet_button_state_list_animator.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true">
-        <set>
-            <objectAnimator
-                android:duration="200"
-                android:propertyName="scaleX"
-                android:valueFrom="1.0"
-                android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
-                android:valueType="floatType"/>
-            <objectAnimator
-                android:duration="200"
-                android:propertyName="scaleY"
-                android:valueFrom="1.0"
-                android:valueTo="@dimen/bottom_sheet_button_selection_scaled"
-                android:valueType="floatType"/>
-        </set>
-    </item>
-    <item android:state_focused="false">
-        <set>
-            <objectAnimator
-                android:duration="200"
-                android:propertyName="scaleX"
-                android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
-                android:valueTo="1.0"
-                android:valueType="floatType"/>
-            <objectAnimator
-                android:duration="200"
-                android:propertyName="scaleY"
-                android:valueFrom="@dimen/bottom_sheet_button_selection_scaled"
-                android:valueTo="1.0"
-                android:valueType="floatType"/>
-        </set>
-    </item>
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
deleted file mode 100644
index cace36d..0000000
--- a/packages/SystemUI/res/anim/tv_bottom_sheet_enter.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:interpolator="@android:interpolator/decelerate_quint">
-    <translate android:fromYDelta="100%"
-               android:toYDelta="0"
-               android:duration="900"/>
-</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml b/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
deleted file mode 100644
index f7efe7cd..0000000
--- a/packages/SystemUI/res/anim/tv_bottom_sheet_exit.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:interpolator="@android:interpolator/decelerate_quint">
-    <translate android:fromYDelta="0"
-               android:toYDelta="100%"
-               android:duration="500"/>
-</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml b/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
deleted file mode 100644
index 94deced..0000000
--- a/packages/SystemUI/res/anim/tv_privacy_chip_collapse.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="collapseProgress"
-    android:interpolator="@interpolator/tv_privacy_chip_collapse_interpolator"
-    android:valueTo="1"
-    android:valueType="floatType"
-    android:duration="@integer/privacy_chip_animation_millis" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml b/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
deleted file mode 100644
index 81c83e2..0000000
--- a/packages/SystemUI/res/anim/tv_privacy_chip_expand.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:propertyName="collapseProgress"
-    android:interpolator="@interpolator/tv_privacy_chip_expand_interpolator"
-    android:valueTo="0"
-    android:valueType="floatType"
-    android:duration="@integer/privacy_chip_animation_millis" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml b/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
deleted file mode 100644
index 05248f1..0000000
--- a/packages/SystemUI/res/color/bottom_sheet_button_text_color.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true"
-          android:color="@color/bottom_sheet_button_text_color_focused"/>
-    <item android:color="@color/bottom_sheet_button_text_color_unfocused"/>
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
similarity index 62%
copy from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
copy to packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
index 9b0bae0..f8d4af5 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
@@ -14,8 +13,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true"
-          android:color="@color/bottom_sheet_button_background_color_focused"/>
-    <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_enabled="false"
+        android:color="?androidprv:attr/materialColorPrimaryFixed"
+        android:alpha="0.30"/>
+    <item android:color="?androidprv:attr/materialColorPrimaryFixed"/>
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
similarity index 62%
copy from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
copy to packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
index 9b0bae0..faba8fc 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_filled_large_text.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
@@ -14,8 +13,10 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true"
-          android:color="@color/bottom_sheet_button_background_color_focused"/>
-    <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_enabled="false"
+        android:color="?androidprv:attr/materialColorOnPrimaryFixed"
+        android:alpha="0.30"/>
+    <item android:color="?androidprv:attr/materialColorOnPrimaryFixed"/>
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
similarity index 63%
copy from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
copy to packages/SystemUI/res/color/qs_dialog_btn_outline.xml
index 9b0bae0..1adfe5b 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_outline.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
@@ -14,8 +13,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true"
-          android:color="@color/bottom_sheet_button_background_color_focused"/>
-    <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_enabled="false"
+        android:color="?androidprv:attr/materialColorPrimary"
+        android:alpha="0.30"/>
+    <item android:color="?androidprv:attr/materialColorPrimary"/>
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml b/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
similarity index 63%
rename from packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
rename to packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
index 9b0bae0..5dc994f23 100644
--- a/packages/SystemUI/res/color/bottom_sheet_button_background_color.xml
+++ b/packages/SystemUI/res/color/qs_dialog_btn_outline_text.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ 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.
@@ -14,8 +13,11 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true"
-          android:color="@color/bottom_sheet_button_background_color_focused"/>
-    <item android:color="@color/bottom_sheet_button_background_color_unfocused"/>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_enabled="false"
+        android:color="?androidprv:attr/materialColorOnSurface"
+        android:alpha="0.30"/>
+    <item android:color="?androidprv:attr/materialColorOnSurface"/>
 </selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_background.xml
deleted file mode 100644
index 87850a0..0000000
--- a/packages/SystemUI/res/drawable/bottom_sheet_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="@color/bottom_sheet_background_color"/>
-    <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml b/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
deleted file mode 100644
index cd2aa9c..0000000
--- a/packages/SystemUI/res/drawable/bottom_sheet_background_with_blur.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="@color/bottom_sheet_background_color_with_blur"/>
-    <corners android:radius="@dimen/bottom_sheet_corner_radius"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml b/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
deleted file mode 100644
index 585a6bc..0000000
--- a/packages/SystemUI/res/drawable/bottom_sheet_button_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
-    <solid android:color="@color/bottom_sheet_button_background_color"/>
-    <corners android:radius="@dimen/bottom_sheet_button_corner_radius"/>
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
index 1590daa..50405ca 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
@@ -26,7 +26,7 @@
     <item>
         <shape android:shape="rectangle">
             <corners android:radius="18dp"/>
-            <solid android:color="?androidprv:attr/materialColorPrimaryFixed"/>
+            <solid android:color="@color/qs_dialog_btn_filled_large_background"/>
         </shape>
     </item>
 </ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index b0dc652..9e9533a 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -15,7 +15,6 @@
   ~ limitations under the License.
   -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
        android:insetTop="@dimen/dialog_button_vertical_inset"
        android:insetBottom="@dimen/dialog_button_vertical_inset">
     <ripple android:color="?android:attr/colorControlHighlight">
@@ -29,7 +28,7 @@
             <shape android:shape="rectangle">
                 <corners android:radius="?android:attr/buttonCornerRadius"/>
                 <solid android:color="@android:color/transparent"/>
-                <stroke android:color="?androidprv:attr/materialColorPrimary"
+                <stroke android:color="@color/qs_dialog_btn_outline"
                         android:width="1dp"
                 />
                 <padding android:left="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml b/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
index 3705d918..264e10a 100644
--- a/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_dialog_background.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle">
 
diff --git a/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml b/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
index 3c4fc05..bc73aab 100644
--- a/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_dialog_circle.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="oval">
   <solid android:color="@color/tv_volume_dialog_circle" />
diff --git a/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml b/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
index 588782d..3c31861 100644
--- a/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
+++ b/packages/SystemUI/res/drawable/tv_volume_row_seek_thumb.xml
@@ -14,6 +14,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
     <solid android:color="@color/tv_volume_dialog_accent" />
     <size android:width="@dimen/tv_volume_seek_bar_thumb_diameter"
diff --git a/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
deleted file mode 100644
index 1a40173..0000000
--- a/packages/SystemUI/res/interpolator/tv_privacy_chip_collapse_interpolator.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-    android:controlX1="0.4"
-    android:controlY1="1.00"
-    android:controlX2="0.12"
-    android:controlY2="1.00"/>
diff --git a/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml b/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
deleted file mode 100644
index ed44715..0000000
--- a/packages/SystemUI/res/interpolator/tv_privacy_chip_expand_interpolator.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 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.
-  -->
-
-<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
-                  android:controlX1="0.12"
-                  android:controlY1="1.00"
-                  android:controlX2="0.4"
-                  android:controlY2="1.00"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
index 991dc63e..bddcb6a 100644
--- a/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout-land-television/volume_dialog_row.xml
@@ -13,6 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:tag="row"
diff --git a/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml b/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml
deleted file mode 100644
index eb21c43..0000000
--- a/packages/SystemUI/res/layout-television/inattentive_sleep_warning.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2019 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/sleep_warning_dialog_container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:theme="@android:style/Theme.DeviceDefault.Dialog"
-    android:focusable="true">
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@android:color/black"
-        android:alpha="?android:backgroundDimAmount" />
-    <LinearLayout
-        android:layout_width="380dp"
-        android:layout_height="wrap_content"
-        android:background="@drawable/rounded_bg_full"
-        android:padding="16dp"
-        android:layout_margin="32dp"
-        android:layout_gravity="bottom|right"
-        android:orientation="vertical">
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/inattentive_sleep_warning_title"
-            android:layout_marginBottom="8dp"
-            android:textColor="?android:attr/textColorPrimary"
-            android:textAppearance="@android:style/TextAppearance.DeviceDefault.Large"/>
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/inattentive_sleep_warning_message"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textAppearance="@android:style/TextAppearance.DeviceDefault"/>
-    </LinearLayout>
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_side_icon.xml b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
index f1b7259..fbcead1 100644
--- a/packages/SystemUI/res/layout/qs_tile_side_icon.xml
+++ b/packages/SystemUI/res/layout/qs_tile_side_icon.xml
@@ -30,12 +30,11 @@
         android:visibility="gone"
     />
 
-    <ImageView
+    <com.android.systemui.qs.tileimpl.ChevronImageView
         android:id="@+id/chevron"
         android:layout_width="@dimen/qs_icon_size"
         android:layout_height="@dimen/qs_icon_size"
         android:src="@*android:drawable/ic_chevron_end"
-        android:autoMirrored="true"
         android:visibility="gone"
         android:importantForAccessibility="no"
     />
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
deleted file mode 100644
index d6c63eb..0000000
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2018, 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.
-*/
--->
-<com.android.systemui.statusbar.StatusBarMobileView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/mobile_combo"
-    android:layout_width="wrap_content"
-    android:layout_height="match_parent"
-    android:gravity="center_vertical" >
-
-    <include layout="@layout/status_bar_mobile_signal_group_inner" />
-
-</com.android.systemui.statusbar.StatusBarMobileView>
-
diff --git a/packages/SystemUI/res/layout/tv_bottom_sheet.xml b/packages/SystemUI/res/layout/tv_bottom_sheet.xml
deleted file mode 100644
index b69cdc7..0000000
--- a/packages/SystemUI/res/layout/tv_bottom_sheet.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/bottom_sheet"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="center_vertical"
-    android:orientation="horizontal"
-    android:minHeight="@dimen/bottom_sheet_min_height"
-    android:paddingHorizontal="@dimen/bottom_sheet_padding_horizontal"
-    android:paddingVertical="@dimen/bottom_sheet_padding_vertical">
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:minWidth="80dp"
-        android:gravity="center"
-        android:orientation="horizontal">
-        <ImageView
-            android:id="@+id/bottom_sheet_icon"
-            android:layout_width="@dimen/bottom_sheet_icon_size"
-            android:layout_height="@dimen/bottom_sheet_icon_size"
-            android:layout_gravity="center_vertical"
-            android:tint="@color/bottom_sheet_icon_color"/>
-        <ImageView
-            android:id="@+id/bottom_sheet_second_icon"
-            android:layout_width="@dimen/bottom_sheet_icon_size"
-            android:layout_height="@dimen/bottom_sheet_icon_size"
-            android:layout_marginStart="@dimen/bottom_sheet_icon_margin"
-            android:layout_gravity="center_vertical"
-            android:tint="@color/bottom_sheet_icon_color"/>
-    </LinearLayout>
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/bottom_sheet_padding_horizontal"
-        android:layout_weight="1"
-        android:orientation="vertical">
-        <TextView
-            android:id="@+id/bottom_sheet_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="@dimen/bottom_sheet_title_margin_bottom"
-            android:textAppearance="@style/BottomSheet.TitleText"/>
-
-        <TextView
-            android:id="@+id/bottom_sheet_body"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginBottom="@dimen/bottom_sheet_details_margin_bottom"
-            android:textAppearance="@style/BottomSheet.BodyText" />
-    </LinearLayout>
-
-    <LinearLayout
-        android:orientation="vertical"
-        android:layout_width="@dimen/bottom_sheet_actions_width"
-        android:layout_height="match_parent"
-        android:gravity="center">
-        <Button
-            android:id="@+id/bottom_sheet_positive_button"
-            style="@style/BottomSheet.ActionItem" />
-        <Space
-            android:layout_width="0dp"
-            android:layout_height="@dimen/bottom_sheet_actions_spacing" />
-        <Button
-            android:id="@+id/bottom_sheet_negative_button"
-            style="@style/BottomSheet.ActionItem" />
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_notification_item.xml b/packages/SystemUI/res/layout/tv_notification_item.xml
deleted file mode 100644
index 711cd4e..0000000
--- a/packages/SystemUI/res/layout/tv_notification_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/tv_notification_panel_width"
-    android:layout_height="wrap_content"
-    android:background="?android:attr/selectableItemBackground"
-    android:orientation="vertical"
-    android:padding="12dp">
-
-    <TextView
-        android:id="@+id/tv_notification_title"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:paddingBottom="12dp"
-        android:textColor="@color/tv_notification_text_color"
-        android:textSize="18sp" />
-
-    <TextView
-        android:id="@+id/tv_notification_details"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:textColor="@color/tv_notification_text_color" />
-
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_notification_panel.xml b/packages/SystemUI/res/layout/tv_notification_panel.xml
deleted file mode 100644
index eae44c8..0000000
--- a/packages/SystemUI/res/layout/tv_notification_panel.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/tv_notification_panel"
-    android:layout_width="@dimen/tv_notification_panel_width"
-    android:layout_height="match_parent"
-    android:layout_gravity="end"
-    android:background="@android:color/transparent"
-    android:orientation="vertical">
-
-    <TextView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:padding="12dp"
-        android:paddingTop="24dp"
-        android:text="@string/tv_notification_panel_title"
-        android:textColor="@color/tv_notification_text_color"
-        android:textSize="24sp"
-        android:textStyle="bold" />
-
-    <TextView
-        android:id="@+id/no_tv_notifications"
-        style="?android:attr/titleTextStyle"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:gravity="top|center"
-        android:paddingTop="24dp"
-        android:text="@string/tv_notification_panel_no_notifications"
-        android:textColor="@color/tv_notification_text_color"
-        android:visibility="gone" />
-
-    <androidx.leanback.widget.VerticalGridView
-        android:id="@+id/notifications_list"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
deleted file mode 100644
index 9069b8f..0000000
--- a/packages/SystemUI/res/layout/tv_ongoing_privacy_chip.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/privacy_chip_height"
-    android:minWidth="@dimen/privacy_chip_dot_bg_width"
-    android:gravity="center">
-    <LinearLayout
-        android:id="@+id/icons_container"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/privacy_chip_height"
-        android:minWidth="@dimen/privacy_chip_dot_bg_width"
-        android:orientation="horizontal"
-        android:gravity="center"
-        android:paddingHorizontal="@dimen/privacy_chip_padding_horizontal"
-        android:clipToPadding="false" />
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/tv_privacy_chip_container.xml b/packages/SystemUI/res/layout/tv_privacy_chip_container.xml
deleted file mode 100644
index 20ffe2b9..0000000
--- a/packages/SystemUI/res/layout/tv_privacy_chip_container.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2022 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:minWidth="@dimen/privacy_chips_max_width"
-    android:orientation="horizontal"
-    android:clipToPadding="false"
-    android:gravity="end"
-    android:padding="@dimen/privacy_chips_bar_padding">
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="@dimen/privacy_chip_height" />
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings_tv.xml b/packages/SystemUI/res/values-af/strings_tv.xml
deleted file mode 100644
index 4fec3b2..0000000
--- a/packages/SystemUI/res/values-af/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is gekoppel"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is ontkoppel"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Kennisgewings"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Geen kennisgewings nie"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofoon neem tans op"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera neem tans op"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera en mikrofoon neem tans op"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofoon het opname gestop"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera het opname gestop"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera en mikrofoon het opname gestop"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Skermopname het begin"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skermopname het gestop"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-am/strings_tv.xml b/packages/SystemUI/res/values-am/strings_tv.xml
deleted file mode 100644
index d3d1433..0000000
--- a/packages/SystemUI/res/values-am/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ተያይዟል"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ተቋርቷል"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"በ<xliff:g id="VPN_APP">%1$s</xliff:g> በኩል"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"ማሳወቂያዎች"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ምንም ማሳወቂያዎች የሉም"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"ማይክሮፎን እየቀዳ ነው"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ካሜራ እየቀረጸ ነው"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ካሜራ እየቀረጸ እና ማይክሮፎን እየቀዳ ነው"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ማይክሮፎን መቅዳት አቁሟል"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ካሜራ መቅረጽ አቁሟል"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ካሜራ መቅረጽ እና ማይክሮፎን መቅዳት አቁመዋል"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"የማያ ገፅ ቀረጻ ተጀምሯል"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"የማያ ገፅ ቀረጻ ቆሟል"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ar/strings_tv.xml b/packages/SystemUI/res/values-ar/strings_tv.xml
deleted file mode 100644
index 0a96069..0000000
--- a/packages/SystemUI/res/values-ar/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"‏شبكة VPN متصلة."</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‏شبكة VPN غير متصلة."</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"عبر <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"الإشعارات"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ما من إشعارات"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"جارٍ التسجيل بالميكرفون"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"جارٍ التسجيل بالكاميرا"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"جارٍ التسجيل بالكاميرا والميكروفون"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"توقف التسجيل بالميكرفون."</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"توقف التسجيل بالكاميرا."</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"توقف التسجيل بالكاميرا والميكروفون."</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"بدأ تسجيل الشاشة."</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"توقَّف تسجيل الشاشة."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-as/strings_tv.xml b/packages/SystemUI/res/values-as/strings_tv.xml
deleted file mode 100644
index ec37d53..0000000
--- a/packages/SystemUI/res/values-as/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"ভিপিএন সংযোগ হৈ আছে"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ভিপিএনৰ সংযোগ বিচ্ছিন্ন হৈছে"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>ৰ জৰিয়তে"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"জাননী"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"কোনো জাননী নাই"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰি আছে"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"কেমেৰাটোৱে ৰেক’ৰ্ড কৰি আছে"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"কেমেৰা আৰু মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰি আছে"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰাটো বন্ধ কৰিছে"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"কেমেৰাটোৱে ৰেক’ৰ্ড কৰাটো বন্ধ কৰিছে"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"কেমেৰা আৰু মাইক্ৰ’ফ’নটোৱে ৰেক’ৰ্ড কৰাটো বন্ধ কৰিছে"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"স্ক্ৰীন ৰেকৰ্ডিং আৰম্ভ কৰা হৈছে"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"স্ক্ৰীন ৰেকৰ্ডিং বন্ধ হৈছে"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-az/strings_tv.xml b/packages/SystemUI/res/values-az/strings_tv.xml
deleted file mode 100644
index 055b2fa..0000000
--- a/packages/SystemUI/res/values-az/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN qoşulub"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ayrılıb"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> vasitəsilə"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirişlər"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildiriş yoxdur"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon yazır"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera yazır"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera və mikrofon yazır"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon yazmağı dayandırıb"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera yazmağı dayandırıb"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera və mikrofon yazmağı dayandırıb"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekranı videoya çəkməyə başlandı"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekranı videoya çəkməyi dayandırdınız"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml b/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
deleted file mode 100644
index f72890bd..0000000
--- a/packages/SystemUI/res/values-b+sr+Latn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je povezan"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Veza sa VPN-om je prekinuta"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Preko: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obaveštenja"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obaveštenja"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Snimanje mikrofonom je zaustavljeno"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Snimanje kamerom je zaustavljeno"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Snimanje kamerom i mikrofonom je zaustavljeno"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje ekrana je započeto"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje ekrana je zaustavljeno"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-be/strings_tv.xml b/packages/SystemUI/res/values-be/strings_tv.xml
deleted file mode 100644
index aee04c2..0000000
--- a/packages/SystemUI/res/values-be/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN падключаны"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN адключаны"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Праз <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Апавяшчэнні"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Апавяшчэнняў няма"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Ідзе запіс з выкарыстаннем мікрафона"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Ідзе запіс з выкарыстаннем камеры"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Ідзе запіс з выкарыстаннем камеры і мікрафона"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Запіс з выкарыстаннем мікрафона спынены"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Запіс з выкарыстаннем камеры спынены"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Запіс з выкарыстаннем камеры і мікрафона спынены"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Пачаўся запіс экрана"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Запіс экрана спынены"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-bg/strings_tv.xml b/packages/SystemUI/res/values-bg/strings_tv.xml
deleted file mode 100644
index 6d578ad..0000000
--- a/packages/SystemUI/res/values-bg/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN е свързана"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Връзката с VPN е прекратена"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Чрез <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Известия"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Няма известия"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофонът записва"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камерата записва"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камерата и микрофонът записват"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофонът спря да записва"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камерата спря да записва"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камерата и микрофонът спряха да записват"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Записването на екрана започна"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Записването на екрана бе спряно"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-bn/strings_tv.xml b/packages/SystemUI/res/values-bn/strings_tv.xml
deleted file mode 100644
index dcaefd97..0000000
--- a/packages/SystemUI/res/values-bn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN কানেক্ট করা হয়েছে"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ডিসকানেক্ট করা হয়েছে"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-এর মাধ্যমে"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"বিজ্ঞপ্তি"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"কোনও বিজ্ঞপ্তি নেই"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"মাইক্রোফোনে রেকর্ড করা হচ্ছে"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ক্যামেরায় রেকর্ড করা হচ্ছে"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ক্যামেরা ও মাইক্রোফোনে রেকর্ড করা হচ্ছে"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"মাইক্রোফোনে রেকর্ড করা বন্ধ হয়ে গেছে"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ক্যামেরায় রেকর্ড করা বন্ধ হয়ে গেছে"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ক্যামেরা ও মাইক্রোফোনে রেকর্ড করা বন্ধ হয়ে গেছে"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"স্ক্রিন রেকর্ড করা শুরু হয়েছে"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"স্ক্রিন রেকর্ড করা বন্ধ হয়েছে"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-bs/strings_tv.xml b/packages/SystemUI/res/values-bs/strings_tv.xml
deleted file mode 100644
index db8c5b3..0000000
--- a/packages/SystemUI/res/values-bs/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je povezan"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Veza s VPN-om je prekinuta"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Putem: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obavještenja"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obavještenja"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon je prestao snimati"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera je prestala snimati"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera i mikrofon su prestali snimati"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje ekrana je pokrenuto"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje ekrana je zaustavljeno"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ca/strings_tv.xml b/packages/SystemUI/res/values-ca/strings_tv.xml
deleted file mode 100644
index 972d38b..0000000
--- a/packages/SystemUI/res/values-ca/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"La VPN està connectada"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"La VPN està desconnectada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Mitjançant <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificacions"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Cap notificació"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"El micròfon està gravant"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"La càmera està gravant"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La càmera i el micròfon estan gravant"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"El micròfon ha deixat de gravar"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La càmera ha deixat de gravar"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La càmera i el micròfon han deixat de gravar"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"La gravació de la pantalla ha començat"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"La gravació de la pantalla s\'ha aturat"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-cs/strings_tv.xml b/packages/SystemUI/res/values-cs/strings_tv.xml
deleted file mode 100644
index ab391f4..0000000
--- a/packages/SystemUI/res/values-cs/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Síť VPN je připojena"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Síť VPN je odpojena"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Přes <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Oznámení"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Žádná oznámení"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon nahrává"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera nahrává"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera a mikrofon nahrávají"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon přestal nahrávat"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera přestala nahrávat"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera a mikrofon přestaly nahrávat"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Zahájeno nahrávání obrazovky"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Záznam obrazovky ukončen"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-da/strings_tv.xml b/packages/SystemUI/res/values-da/strings_tv.xml
deleted file mode 100644
index 66b7964..0000000
--- a/packages/SystemUI/res/values-da/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN er tilsluttet"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN er afbrudt"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifikationer"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ingen notifikationer"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonen optager"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kameraet optager"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kameraet og mikrofonen optager"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonen er stoppet med at optage"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kameraet er stoppet med at optage"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kameraet og mikrofonen er stoppet med at optage"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Skærmoptagelsen er startet"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skærmoptagelsen er stoppet"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-de/strings_tv.xml b/packages/SystemUI/res/values-de/strings_tv.xml
deleted file mode 100644
index 5df9d32..0000000
--- a/packages/SystemUI/res/values-de/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ist verbunden"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ist nicht verbunden"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Über <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Benachrichtigungen"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Keine Benachrichtigungen"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon nimmt auf"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera nimmt auf"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera und Mikrofon nehmen auf"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Aufnahme des Mikrofons gestoppt"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Aufnahme der Kamera gestoppt"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Aufnahme von Kamera und Mikrofon gestoppt"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Bildschirmaufzeichnung gestartet"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Bildschirmaufzeichnung beendet"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-el/strings_tv.xml b/packages/SystemUI/res/values-el/strings_tv.xml
deleted file mode 100644
index 9fb126d..0000000
--- a/packages/SystemUI/res/values-el/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Το VPN συνδέθηκε"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Το VPN αποσυνδέθηκε"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Μέσω <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Ειδοποιήσεις"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Δεν υπάρχουν ειδοποιήσεις."</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Πραγματοποιείται εγγραφή από το μικρόφωνο"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Πραγματοποιείται εγγραφή από την κάμερα"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Πραγματοποιείται εγγραφή από την κάμερα και το μικρόφωνο"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Η εγγραφή από το μικρόφωνο διακόπηκε"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Η εγγραφή από την κάμερα διακόπηκε"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Η εγγραφή από την κάμερα και το μικρόφωνο διακόπηκε"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Η εγγραφή οθόνης ξεκίνησε"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Η εγγραφή οθόνης σταμάτησε"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/strings_tv.xml b/packages/SystemUI/res/values-en-rAU/strings_tv.xml
deleted file mode 100644
index e97dbe4..0000000
--- a/packages/SystemUI/res/values-en-rAU/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No notifications"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and microphone are recording"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and microphone stopped recording"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings_tv.xml b/packages/SystemUI/res/values-en-rCA/strings_tv.xml
deleted file mode 100644
index a628846..0000000
--- a/packages/SystemUI/res/values-en-rCA/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No Notifications"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and Microphone are recording"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and Microphone stopped recording"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings_tv.xml b/packages/SystemUI/res/values-en-rGB/strings_tv.xml
deleted file mode 100644
index e97dbe4..0000000
--- a/packages/SystemUI/res/values-en-rGB/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No notifications"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and microphone are recording"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and microphone stopped recording"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/strings_tv.xml b/packages/SystemUI/res/values-en-rIN/strings_tv.xml
deleted file mode 100644
index e97dbe4..0000000
--- a/packages/SystemUI/res/values-en-rIN/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN is connected"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN is disconnected"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No notifications"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Microphone is recording"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera is recording"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera and microphone are recording"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microphone stopped recording"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera stopped recording"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera and microphone stopped recording"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Screen recording started"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Screen recording stopped"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings_tv.xml b/packages/SystemUI/res/values-en-rXC/strings_tv.xml
deleted file mode 100644
index 06116ed..0000000
--- a/packages/SystemUI/res/values-en-rXC/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‏‏‏‎‏‏‎‏‎‏‎‎VPN is connected‎‏‎‎‏‎"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎VPN is disconnected‎‏‎‎‏‎"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎Via ‎‏‎‎‏‏‎<xliff:g id="VPN_APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‏‎‎Notifications‎‏‎‎‏‎"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‎‏‏‎‎No Notifications‎‏‎‎‏‎"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‏‏‎Microphone is recording‎‏‎‎‏‎"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎Camera is recording‎‏‎‎‏‎"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎Camera and Microphone are recording‎‏‎‎‏‎"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‎‏‎‎Microphone stopped recording‎‏‎‎‏‎"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎Camera stopped recording‎‏‎‎‏‎"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‏‏‎‎‎‏‎‎‎Camera and Microphone stopped recording‎‏‎‎‏‎"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎Screen recording started‎‏‎‎‏‎"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎Screen recording stopped‎‏‎‎‏‎"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/strings_tv.xml b/packages/SystemUI/res/values-es-rUS/strings_tv.xml
deleted file mode 100644
index 513e0c7..0000000
--- a/packages/SystemUI/res/values-es-rUS/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"La VPN está conectada."</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"La VPN está desconectada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A través de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificaciones"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"No hay notificaciones"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"El micrófono está grabando"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"La cámara está grabando"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La cámara y el micrófono están grabando"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"El micrófono dejó de grabar"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La cámara dejó de grabar"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La cámara y el micrófono dejaron de grabar"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Se inició la grabación de pantalla"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Se detuvo la grabación de pantalla"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-es/strings_tv.xml b/packages/SystemUI/res/values-es/strings_tv.xml
deleted file mode 100644
index 1482fd0..0000000
--- a/packages/SystemUI/res/values-es/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"La VPN está conectada"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"La VPN está desconectada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A través de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificaciones"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Sin notificaciones"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"El micrófono está grabando"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"La cámara está grabando"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La cámara y el micrófono están grabando"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"El micrófono ha dejado de grabar"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La cámara ha dejado de grabar"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La cámara y el micrófono han dejado de grabar"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Grabación de pantalla iniciada"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Grabación de pantalla detenida"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-et/strings_tv.xml b/packages/SystemUI/res/values-et/strings_tv.xml
deleted file mode 100644
index ee7bba8..0000000
--- a/packages/SystemUI/res/values-et/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN on ühendatud"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-i ühendus on katkestatud"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Teenuse <xliff:g id="VPN_APP">%1$s</xliff:g> kaudu"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Märguanded"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Märguandeid pole"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon salvestab"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kaamera salvestab"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kaamera ja mikrofon salvestavad"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon peatas salvestamise"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kaamera peatas salvestamise"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kaamera ja mikrofon peatasid salvestamise"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekraanikuva salvestamine algas"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekraanikuva salvestamine on peatatud"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-eu/strings_tv.xml b/packages/SystemUI/res/values-eu/strings_tv.xml
deleted file mode 100644
index de5249b..0000000
--- a/packages/SystemUI/res/values-eu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN sarera konektatuta dago"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ez dago sarera konektatuta"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> bidez"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Jakinarazpenak"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ez dago jakinarazpenik"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonoa grabatzen ari da"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera grabatzen ari da"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera eta mikrofonoa grabatzen ari dira"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonoak grabatzeari utzi dio"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamerak grabatzeari utzi dio"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamerak eta mikrofonoak grabatzeari utzi diote"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Hasi da pantailaren grabaketa"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gelditu da pantailaren grabaketa"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fa/strings_tv.xml b/packages/SystemUI/res/values-fa/strings_tv.xml
deleted file mode 100644
index 89f57af..0000000
--- a/packages/SystemUI/res/values-fa/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"‏VPN متصل است"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‏VPN قطع است"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"ازطریق <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"اعلان‌ها"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"اعلانی ندارید"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"میکروفون درحال ضبط کردن است"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"دوربین درحال ضبط کردن است"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"دوربین و میکروفون درحال ضبط کردن هستند"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ضبط میکروفون متوقف شد"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ضبط دوربین متوقف شد"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ضبط دوربین و میکروفون متوقف شد"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ضبط صفحه شروع شد"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ضبط صفحه متوقف شد"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fi/strings_tv.xml b/packages/SystemUI/res/values-fi/strings_tv.xml
deleted file mode 100644
index 61198d4..0000000
--- a/packages/SystemUI/res/values-fi/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN on yhdistetty"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ei ole yhdistettynä"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Palvelun <xliff:g id="VPN_APP">%1$s</xliff:g> kautta"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Ilmoitukset"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ei ilmoituksia"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofoni tallentaa"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera kuvaa"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera ja mikrofoni tallentavat"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofoni lopetti tallentamisen"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera lopetti kuvaamisen"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera ja mikrofoni lopettivat tallentamisen"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Näytön tallennus aloitettu"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Näytön tallennus lopetettu"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml b/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
deleted file mode 100644
index 7ab63af..0000000
--- a/packages/SystemUI/res/values-fr-rCA/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"RPV connecté"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"RPV déconnecté"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Par <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Aucune notification"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Le microphone enregistre"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"L\'appareil photo enregistre"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"L\'appareil photo et le microphone enregistrent"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Le microphone a arrêté l\'enregistrement"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"L\'appareil photo a arrêté l\'enregistrement"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"L\'appareil photo et le microphone ont arrêté l\'enregistrement"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"L\'enregistrement d\'écran a commencé"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"L\'enregistrement d\'écran s\'est arrêté"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-fr/strings_tv.xml b/packages/SystemUI/res/values-fr/strings_tv.xml
deleted file mode 100644
index d1ac554..0000000
--- a/packages/SystemUI/res/values-fr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN connecté"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN déconnecté"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifications"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Aucune notification"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Le micro enregistre…"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"La caméra enregistre…"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"La caméra et le micro enregistrent…"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Le micro a arrêté l\'enregistrement"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"La caméra a arrêté l\'enregistrement"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"La caméra et le micro ont arrêté l\'enregistrement"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Enregistrement de l\'écran démarré"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Enregistrement de l\'écran arrêté"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-gl/strings_tv.xml b/packages/SystemUI/res/values-gl/strings_tv.xml
deleted file mode 100644
index 58279ee..0000000
--- a/packages/SystemUI/res/values-gl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está conectada"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desconectada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A través de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificacións"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Non hai notificacións"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"O micrófono está gravando"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"A cámara está gravando"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A cámara e o micrófono están gravando"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O micrófono deixou de gravar"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A cámara deixou de gravar"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A cámara e o micrófono deixaron de gravar"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Iniciouse a gravación da pantalla"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Parouse a gravación da pantalla"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-gu/strings_tv.xml b/packages/SystemUI/res/values-gu/strings_tv.xml
deleted file mode 100644
index 096a93a..0000000
--- a/packages/SystemUI/res/values-gu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN કનેક્ટ કરેલું છે"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ડિસ્કનેક્ટ કરેલું છે"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> મારફતે"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"નોટિફિકેશન"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"કોઈ નોટિફિકેશન નથી"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"માઇક્રોફોનનું રેકોર્ડિંગ ચાલુ છે"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"કૅમેરાનું રેકોર્ડિંગ ચાલુ છે"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"કૅમેરા અને માઇક્રોફોનનું રેકોર્ડિંગ ચાલુ છે"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"માઇક્રોફોનનું રેકોર્ડિંગ બંધ થઈ ગયું"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"કૅમેરાનું રેકોર્ડિંગ બંધ થઈ ગયું"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"કૅમેરા અને માઇક્રોફોનનું રેકોર્ડિંગ બંધ થઈ ગયું"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"સ્ક્રીન રેકોર્ડિંગ શરૂ થયું"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"સ્ક્રીન રેકોર્ડિંગ બંધ કર્યું"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hi/strings_tv.xml b/packages/SystemUI/res/values-hi/strings_tv.xml
deleted file mode 100644
index 0e5e1225..0000000
--- a/packages/SystemUI/res/values-hi/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"वीपीएन कनेक्ट हो गया है"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"वीपीएन डिसकनेक्ट हो गया है"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> के ज़रिए"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"सूचनाएं"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"कोई सूचना नहीं है"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"माइक्रोफ़ोन रिकॉर्ड कर रहा है"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"कैमरा रिकॉर्ड कर रहा है"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"कैमरा और माइक्रोफ़ोन रिकॉर्ड कर रहे हैं"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"माइक्रोफ़ोन ने रिकॉर्ड करना बंद कर दिया है"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"कैमरे ने रिकॉर्ड करना बंद कर दिया है"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"कैमरे और माइक्रोफ़ोन ने रिकॉर्ड करना बंद कर दिया है"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"स्क्रीन रिकॉर्डिंग शुरू की गई"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"स्क्रीन रिकॉर्डिंग बंद की गई"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hr/strings_tv.xml b/packages/SystemUI/res/values-hr/strings_tv.xml
deleted file mode 100644
index 61cf646..0000000
--- a/packages/SystemUI/res/values-hr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN je spojen"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN je isključen"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Putem mreže <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obavijesti"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nema obavijesti"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon snima"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera snima"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera i mikrofon snimaju"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon je prestao snimati"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera je prestala snimati"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera i mikrofon prestali su snimati"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Snimanje zaslona je počelo"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snimanje zaslona je zaustavljeno"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hu/strings_tv.xml b/packages/SystemUI/res/values-hu/strings_tv.xml
deleted file mode 100644
index f251c38..0000000
--- a/packages/SystemUI/res/values-hu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN-kapcsolat létrejött"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN-kapcsolat megszakadt"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Ezzel: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Értesítések"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nincs értesítés"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"A mikrofon felvételt készít…"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"A kamera felvételt készít…"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A kamera és a mikrofon felvételt készít…"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"A mikrofon befejezte a felvételkészítést"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A kamera befejezte a felvételkészítést"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A kamera és a mikrofon befejezte a felvételkészítést"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"A képernyő rögzítése elindult"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"A képernyő rögzítése leállt"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-hy/strings_tv.xml b/packages/SystemUI/res/values-hy/strings_tv.xml
deleted file mode 100644
index b6a2e13..0000000
--- a/packages/SystemUI/res/values-hy/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-ը միացված է"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-ն անջատված է"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ի միջոցով"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Ծանուցումներ"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ծանուցումներ չկան"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Խոսափողը ձայնագրում է"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Տեսախցիկը տեսագրում է"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Տեսախցիկն ու խոսափողը տեսաձայնագրում են"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Խոսափողն այլևս չի ձայնագրում"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Տեսախցիկն այլևս չի տեսագրում"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Տեսախցիկն ու խոսափողը այլևս չեն տեսաձայնագրում"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Էկրանի տեսագրումը սկսվեց"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Էկրանի տեսագրումը կանգնեցվեց"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-in/strings_tv.xml b/packages/SystemUI/res/values-in/strings_tv.xml
deleted file mode 100644
index 1b8261e7..0000000
--- a/packages/SystemUI/res/values-in/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN terhubung"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN terputus"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Melalui <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifikasi"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Tidak Ada Notifikasi"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon sedang merekam"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera sedang merekam"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera dan Mikrofon sedang merekam"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon berhenti merekam"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera berhenti merekam"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera dan Mikrofon berhenti merekam"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Perekaman layar dimulai"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Perekaman layar dihentikan"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-is/strings_tv.xml b/packages/SystemUI/res/values-is/strings_tv.xml
deleted file mode 100644
index 7150fc8..0000000
--- a/packages/SystemUI/res/values-is/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN er tengt"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN er ekki tengt"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Í gegnum <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Tilkynningar"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Engar tilkynningar"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Hljóðnemi er að taka upp"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Myndavél er að taka upp"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Myndavél og hljóðnemi eru að taka upp"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Hljóðnemi er hættur að taka upp"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Myndavél er hætt að taka upp"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Myndavél og hljóðnemi eru hætt að taka upp"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Skjáupptaka er hafin"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skjáupptöku er lokið"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-it/strings_tv.xml b/packages/SystemUI/res/values-it/strings_tv.xml
deleted file mode 100644
index ab9ee78..0000000
--- a/packages/SystemUI/res/values-it/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN connessa"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN disconnessa"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Tramite <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notifiche"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nessuna notifica"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Registrazione in corso con il microfono"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Registrazione in corso con la fotocamera"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Registrazione in corso con fotocamera e microfono"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Registrazione con il microfono interrotta"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Registrazione con la fotocamera interrotta"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Registrazione con fotocamera e microfono interrotta"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Registrazione dello schermo avviata"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Registrazione dello schermo interrotta"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml
deleted file mode 100644
index 1085505..0000000
--- a/packages/SystemUI/res/values-iw/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"‏ה-VPN מחובר"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‏ה-VPN מנותק"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"דרך <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"התראות"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"אין התראות"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"המיקרופון מקליט"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"המצלמה מקליטה"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"המצלמה והמיקרופון מקליטים"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"המיקרופון הפסיק להקליט"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"המצלמה הפסיקה להקליט"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"המצלמה והמיקרופון הפסיקו להקליט"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"הקלטת המסך החלה"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"הקלטת המסך הופסקה"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ja/strings_tv.xml b/packages/SystemUI/res/values-ja/strings_tv.xml
deleted file mode 100644
index 6b68c73..0000000
--- a/packages/SystemUI/res/values-ja/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN に接続しました"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN に接続していません"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> 経由"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"通知はありません"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"マイクで録音しています"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"カメラで録画しています"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"カメラとマイクで録画、録音しています"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"マイクが録音を停止しました"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"カメラが録画を停止しました"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"カメラとマイクが録画、録音を停止しました"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"画面の録画を開始しました"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"画面の録画を停止しました"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ka/strings_tv.xml b/packages/SystemUI/res/values-ka/strings_tv.xml
deleted file mode 100644
index dcaa138..0000000
--- a/packages/SystemUI/res/values-ka/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN დაკავშირებულია"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN გათიშულია"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-ის მიერ"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"შეტყობინებები"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"შეტყობინებები არ არის"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"მიკროფონი იწერს"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"კამერა იწერს"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"კამერა და მიკროფონი იწერს"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"მიკროფონმა ჩაწერა შეწყვიტა"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"კამერამ ჩაწერა შეწყვიტა"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"კამერამ და მიკროფონმა ჩაწერა შეწყვიტა"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ეკრანის ჩაწერა დაიწყო"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ეკრანის ჩაწერა დასრულდა"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-kk/strings_tv.xml b/packages/SystemUI/res/values-kk/strings_tv.xml
deleted file mode 100644
index bb0e06b..0000000
--- a/packages/SystemUI/res/values-kk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN қосылған"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ажыратылған"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> арқылы жалғанған"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Хабарландырулар"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Хабарландырулар жоқ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон жазып жатыр."</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера жазып жатыр."</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера мен микрофон жазып жатыр."</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофон жазуды тоқтатты."</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камера жазуды тоқтатты."</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камера мен микрофон жазуды тоқтатты."</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Экранды бейнеге жазу басталды."</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Экранды бейнеге жазу тоқтатылды."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-km/strings_tv.xml b/packages/SystemUI/res/values-km/strings_tv.xml
deleted file mode 100644
index 1be9d48..0000000
--- a/packages/SystemUI/res/values-km/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ត្រូវបានភ្ជាប់"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ត្រូវបានផ្ដាច់"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"តាម​រយៈ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"ការ​ជូនដំណឹង"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"គ្មាន​ការជូនដំណឹងទេ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"មីក្រូហ្វូនកំពុងថត"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"កាមេរ៉ាកំពុងថត"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"កាមេរ៉ា និងមីក្រូហ្វូនកំពុងថត"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"មីក្រូហ្វូនបានឈប់ថត"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"កាមេរ៉ាបានឈប់ថត"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"កាមេរ៉ា និងមីក្រូហ្វូនបានឈប់ថត"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"មុខងារថតវីដេអូអេក្រង់​បាន​ចាប់ផ្ដើម"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"មុខងារថតវីដេអូអេក្រង់​​បាន​បញ្ឈប់"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-kn/strings_tv.xml b/packages/SystemUI/res/values-kn/strings_tv.xml
deleted file mode 100644
index cfcc896..0000000
--- a/packages/SystemUI/res/values-kn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ಕನೆಕ್ಷನ್ ಕಡಿತಗೊಂಡಿದೆ"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ಮೂಲಕ"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"ಅಧಿಸೂಚನೆಗಳು"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ಯಾವುದೇ ಅಧಿಸೂಚನೆಗಳಿಲ್ಲ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ಕ್ಯಾಮರಾ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿದೆ"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ಆಗುತ್ತಿವೆ"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಿದೆ"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ಕ್ಯಾಮರಾ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೊಫೋನ್ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಿವೆ"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭವಾಗಿದೆ"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ನಿಲ್ಲಿಸಲಾಗಿದೆ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ko/strings_tv.xml b/packages/SystemUI/res/values-ko/strings_tv.xml
deleted file mode 100644
index 2412ae8..0000000
--- a/packages/SystemUI/res/values-ko/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN에 연결됨"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 연결이 해제됨"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>에 연결됨"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"알림"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"알림 없음"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"마이크 녹음 중"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"카메라 녹화 중"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"카메라 녹화 및 마이크 녹음 중"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"마이크 녹음 중단됨"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"카메라 녹화 중단됨"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"카메라 녹화 및 마이크 녹음 중단됨"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"화면 녹화가 시작되었습니다."</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"화면 녹화가 중지되었습니다."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ky/strings_tv.xml b/packages/SystemUI/res/values-ky/strings_tv.xml
deleted file mode 100644
index 7ad134a..0000000
--- a/packages/SystemUI/res/values-ky/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN туташтырылды"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ажыратылды"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> аркылуу"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Билдирмелер"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Билдирме жок"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон жаздырууда"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера жаздырууда"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера менен микрофон жаздырууда"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофон жаздырууну токтотту"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камера жаздырууну токтотту"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камера менен микрофон жаздырууну токтотту"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Экрандан видео жаздырылып башталды"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Экрандан видео жаздыруу токтотулду"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml
index 8fc4612..52f591f 100644
--- a/packages/SystemUI/res/values-land-television/dimens.xml
+++ b/packages/SystemUI/res/values-land-television/dimens.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <resources>
   <!-- Height of volume bar -->
   <dimen name="volume_dialog_panel_height">190dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings_tv.xml b/packages/SystemUI/res/values-lo/strings_tv.xml
deleted file mode 100644
index 759931d..0000000
--- a/packages/SystemUI/res/values-lo/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"ເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ຕັດການເຊື່ອມຕໍ່ VPN ແລ້ວ"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"ຜ່ານ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"ການແຈ້ງເຕືອນ"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ບໍ່ມີການແຈ້ງເຕືອນ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"ໄມໂຄຣໂຟນກຳລັງບັນທຶກ"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ກ້ອງຖ່າຍຮູບກຳລັງບັນທຶກ"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ກ້ອງຖ່າຍຮູບ ແລະ ໄມໂຄຣໂຟນກຳລັງບັນທຶກ"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ໄມໂຄຣໂຟນຢຸດການບັນທຶກແລ້ວ"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ກ້ອງຖ່າຍຮູບຢຸດການບັນທຶກແລ້ວ"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ກ້ອງຖ່າຍຮູບ ແລະ ໄມໂຄຣໂຟນຢຸດການບັນທຶກແລ້ວ"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ເລີ່ມການບັນທຶກໜ້າຈໍແລ້ວ"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ຢຸດການບັນທຶກໜ້າຈໍແລ້ວ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-lt/strings_tv.xml b/packages/SystemUI/res/values-lt/strings_tv.xml
deleted file mode 100644
index 93c77f5..0000000
--- a/packages/SystemUI/res/values-lt/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN prijungtas"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN atjungtas"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Per „<xliff:g id="VPN_APP">%1$s</xliff:g>“"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Pranešimai"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nėra jokių pranešimų"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonas įrašo"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera įrašo"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera ir mikrofonas įrašo"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonas nebeįrašo"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera nebeįrašo"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera ir mikrofonas nebeįrašo"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekrano vaizdo įrašymas pradėtas"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekrano vaizdo įrašymas sustabdytas"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-lv/strings_tv.xml b/packages/SystemUI/res/values-lv/strings_tv.xml
deleted file mode 100644
index da64f32..0000000
--- a/packages/SystemUI/res/values-lv/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Savienojums ar VPN ir izveidots."</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Savienojums ar VPN ir pārtraukts."</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Izmantojot: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Paziņojumi"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nav paziņojumu"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Notiek ierakstīšana ar mikrofonu"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Notiek ierakstīšana ar kameru"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Notiek ierakstīšana ar kameru un mikrofonu"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Ierakstīšana ar mikrofonu apturēta"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Ierakstīšana ar kameru apturēta"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Ierakstīšana ar kameru un mikrofonu apturēta"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekrāna ierakstīšana ir sākta."</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekrāna ierakstīšana ir apturēta."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-mk/strings_tv.xml b/packages/SystemUI/res/values-mk/strings_tv.xml
deleted file mode 100644
index 529d19d..0000000
--- a/packages/SystemUI/res/values-mk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN е поврзана"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN е исклучена"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Преку <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Известувања"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Нема известувања"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофонот снима"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камерата снима"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камерата и микрофонот снимаат"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофонот прекина со снимање"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камерата прекина со снимање"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камерата и микрофонот прекинаа со снимање"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Снимањето екран започна"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Снимањето екран сопре"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ml/strings_tv.xml b/packages/SystemUI/res/values-ml/strings_tv.xml
deleted file mode 100644
index 2faccb8..0000000
--- a/packages/SystemUI/res/values-ml/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN കണക്റ്റ് ചെയ്‌തു"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN വിച്ഛേദിച്ചു"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> വഴി"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"അറിയിപ്പുകൾ"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"അറിയിപ്പുകൾ ഒന്നുമില്ല"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"മൈക്രോഫോൺ റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ക്യാമറ റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ക്യാമറയും മൈക്രോഫോണും റെക്കോർഡ് ചെയ്യുകയാണ്"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"മൈക്രോഫോൺ റെക്കോർഡ് ചെയ്യുന്നത് നിർത്തി"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ക്യാമറ റെക്കോർഡ് ചെയ്യുന്നത് നിർത്തി"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ക്യാമറയും മൈക്രോഫോണും റെക്കോർഡ് ചെയ്യുന്നത് നിർത്തി"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"സ്ക്രീൻ റെക്കോർഡിംഗ് ആരംഭിച്ചു"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"സ്‌ക്രീൻ റെക്കോർഡിംഗ് നിർത്തി"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-mn/strings_tv.xml b/packages/SystemUI/res/values-mn/strings_tv.xml
deleted file mode 100644
index c9b667c..0000000
--- a/packages/SystemUI/res/values-mn/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN холбогдсон"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN салсан"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g>-р"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Мэдэгдэл"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Мэдэгдэл байхгүй байна"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон бичиж байна"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камер бичиж байна"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камер болон микрофон бичиж байна"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Микрофон бичихээ зогсоосон"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камер бичихээ зогсоосон"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камер болон микрофон бичихээ зогсоосон"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Дэлгэцийн бичлэгийг эхлүүлсэн"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Дэлгэцийн бичлэгийг зогсоосон"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-mr/strings_tv.xml b/packages/SystemUI/res/values-mr/strings_tv.xml
deleted file mode 100644
index 74d60cd..0000000
--- a/packages/SystemUI/res/values-mr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN कनेक्‍ट केले"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN डिस्कनेक्ट केले"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> द्वारे"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"सूचना"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"सूचना नाहीत"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"मायक्रोफोन रेकॉर्ड करत आहे"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"कॅमेरा रेकॉर्ड करत आहे"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"कॅमेरा आणि मायक्रोफोन रेकॉर्ड करत आहेत"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"मायक्रोफोनने रेकॉर्ड करणे थांबवले"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"कॅमेराने रेकॉर्ड करणे थांबवले"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"कॅमेरा आणि मायक्रोफोनने रेकॉर्ड करणे थांबवले"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"स्क्रीन रेकॉर्डिंग सुरू झाले आहे"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"स्क्रीन रेकॉर्डिंग थांबले आहे"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ms/strings_tv.xml b/packages/SystemUI/res/values-ms/strings_tv.xml
deleted file mode 100644
index 08b9de0..0000000
--- a/packages/SystemUI/res/values-ms/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN telah disambungkan"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN diputuskan sambungan"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Melalui <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Pemberitahuan"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Tiada Pemberitahuan"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon sedang merakam"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera sedang merakam"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera dan Mikrofon sedang merakam"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon berhenti merakam"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera berhenti merakam"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera dan Mikrofon berhenti merakam"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Rakaman skrin dimulakan"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Rakaman skrin dihentikan"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-my/strings_tv.xml b/packages/SystemUI/res/values-my/strings_tv.xml
deleted file mode 100644
index 0ac3950..0000000
--- a/packages/SystemUI/res/values-my/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ချိတ်ဆက်ထားသည်"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ချိတ်ဆက်မှုမရှိပါ"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> မှတစ်ဆင့်"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"အကြောင်းကြားချက်များ"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"အကြောင်းကြားချက်များ မရှိပါ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"မိုက်ခရိုဖုန်း မှတ်တမ်းတင်နေသည်"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ကင်မရာ မှတ်တမ်းတင်နေသည်"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ကင်မရာနှင့် မိုက်ခရိုဖုန်းက မှတ်တမ်းတင်နေသည်"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"မိုက်ခရိုဖုန်း မှတ်တမ်းတင်ခြင်းကို ရပ်ထားသည်"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ကင်မရာ မှတ်တမ်းတင်ခြင်းကို ရပ်ထားသည်"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ကင်မရာနှင့် မိုက်ခရိုဖုန်းက မှတ်တမ်းတင်ခြင်းကို ရပ်ထားသည်"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"မျက်နှာပြင် ရိုက်ကူးမှု စတင်လိုက်ပါပြီ"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"မျက်နှာပြင် ရိုက်ကူးမှုကို ရပ်လိုက်ပါပြီ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-nb/strings_tv.xml b/packages/SystemUI/res/values-nb/strings_tv.xml
deleted file mode 100644
index 5848f58..0000000
--- a/packages/SystemUI/res/values-nb/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN er tilkoblet"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN er frakoblet"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Varsler"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ingen varsler"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonen tar opp"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kameraet tar opp"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kameraet og mikrofonen tar opp"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonen stoppet opptaket"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kameraet stoppet opptaket"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kameraet og mikrofonen stoppet opptaket"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Skjermopptaket er startet"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skjermopptaket er stoppet"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ne/strings_tv.xml b/packages/SystemUI/res/values-ne/strings_tv.xml
deleted file mode 100644
index ffc315b..0000000
--- a/packages/SystemUI/res/values-ne/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN कनेक्ट गरिएको छ"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN डिस्कनेक्ट गरिएको छ"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> मार्फत"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"सूचनाहरू"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"कुनै पनि सूचना छैन"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"माइक्रोफोनले रेकर्ड गर्दै छ"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"क्यामेराले रेकर्ड गर्दै छ"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"क्यामेरा र माइक्रोफोनले रेकर्ड गर्दै छन्"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"माइक्रोफोनले रेकर्ड गर्न छाड्यो"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"क्यामेराले रेकर्ड गर्न छाड्यो"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"क्यामेरा र माइक्रोफोनले रेकर्ड गर्न छाडे"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"स्क्रिन रेकर्ड गर्न थालियो"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"स्क्रिन रेकर्ड गर्न छाडियो"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-nl/strings_tv.xml b/packages/SystemUI/res/values-nl/strings_tv.xml
deleted file mode 100644
index e018d97..0000000
--- a/packages/SystemUI/res/values-nl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Verbinding met VPN"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Geen verbinding met VPN"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Meldingen"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Geen meldingen"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Microfoon neemt op"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera neemt op"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera en microfoon nemen op"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microfoon neemt niet meer op"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera neemt niet meer op"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera en microfoon nemen niet meer op"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Schermopname gestart"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Schermopname gestopt"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-or/strings_tv.xml b/packages/SystemUI/res/values-or/strings_tv.xml
deleted file mode 100644
index 178bd4b..0000000
--- a/packages/SystemUI/res/values-or/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ସଂଯୋଗ କରାଯାଇଛି"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ବିଚ୍ଛିନ୍ନ କରାଯାଇଛି"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ମାଧ୍ୟମରେ"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ କରୁଛି"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"କ୍ୟାମେରା ରେକର୍ଡିଂ କରୁଛି"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ କରୁଛି"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ ବନ୍ଦ କରିଛି"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"କ୍ୟାମେରା ରେକର୍ଡିଂ ବନ୍ଦ କରିଛି"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନ୍ ରେକର୍ଡିଂ ବନ୍ଦ କରିଛି"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ଆରମ୍ଭ କରାଯାଇଛି"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ସ୍କ୍ରିନ ରେକର୍ଡିଂ ବନ୍ଦ କରାଯାଇଛି"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pa/strings_tv.xml b/packages/SystemUI/res/values-pa/strings_tv.xml
deleted file mode 100644
index 4939a73..0000000
--- a/packages/SystemUI/res/values-pa/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ਕਨੈਕਟ ਹੈ"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN ਡਿਸਕਨੈਕਟ ਹੈ"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ਰਾਹੀਂ"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"ਸੂਚਨਾਵਾਂ"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ਕੋਈ ਸੂਚਨਾ ਨਹੀਂ"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਰਿਕਾਰਡ ਕਰ ਰਿਹਾ ਹੈ"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"ਕੈਮਰਾ ਰਿਕਾਰਡ ਕਰ ਰਿਹਾ ਹੈ"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਰਿਕਾਰਡ ਕਰ ਰਹੇ ਹਨ"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੇ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"ਕੈਮਰੇ ਨੇ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੇ ਰਿਕਾਰਡ ਕਰਨਾ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਸ਼ੁਰੂ ਹੋਈ"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਬੰਦ ਹੋਈ"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pl/strings_tv.xml b/packages/SystemUI/res/values-pl/strings_tv.xml
deleted file mode 100644
index 98db830..0000000
--- a/packages/SystemUI/res/values-pl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Połączono z VPN"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Rozłączono z VPN"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Przez: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Powiadomienia"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Brak powiadomień"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon rejestruje"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Aparat rejestruje"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Aparat i mikrofon rejestrują"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon przestał rejestrować"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Aparat przestał rejestrować"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Aparat i mikrofon przestały rejestrować"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Rozpoczęto nagrywanie ekranu"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Zatrzymano nagrywanie ekranu"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml b/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
deleted file mode 100644
index 6c5ff0c..0000000
--- a/packages/SystemUI/res/values-pt-rBR/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está conectada"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desconectada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificações"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nenhuma notificação"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"O microfone está gravando"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"A câmera está gravando"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A câmera e o microfone estão gravando"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O microfone parou de gravar"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A câmera parou de gravar"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A câmera e o microfone pararam de gravar"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Gravação de tela iniciada"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gravação de tela interrompida"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml b/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
deleted file mode 100644
index c3e6c43..0000000
--- a/packages/SystemUI/res/values-pt-rPT/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está ligada"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desligada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Através de <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificações"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Sem notificações"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"O microfone está a gravar"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"A câmara está a gravar"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A câmara e o microfone estão a gravar"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O microfone parou a gravação"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A câmara parou a gravação"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A câmara e o microfone pararam a gravação"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Gravação de ecrã iniciada"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gravação de ecrã parada"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-pt/strings_tv.xml b/packages/SystemUI/res/values-pt/strings_tv.xml
deleted file mode 100644
index 6c5ff0c..0000000
--- a/packages/SystemUI/res/values-pt/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN está conectada"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN está desconectada"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificações"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nenhuma notificação"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"O microfone está gravando"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"A câmera está gravando"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"A câmera e o microfone estão gravando"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"O microfone parou de gravar"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"A câmera parou de gravar"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"A câmera e o microfone pararam de gravar"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Gravação de tela iniciada"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Gravação de tela interrompida"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ro/strings_tv.xml b/packages/SystemUI/res/values-ro/strings_tv.xml
deleted file mode 100644
index 991eef6..0000000
--- a/packages/SystemUI/res/values-ro/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN este conectat"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN este deconectat"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prin <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Notificări"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nicio notificare"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Microfonul înregistrează"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Camera foto înregistrează"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Camera foto și microfonul înregistrează"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Microfonul nu mai înregistrează"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Camera foto a oprit înregistrarea"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Camera foto și microfonul nu mai înregistrează"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Înregistrarea ecranului a început"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Înregistrarea ecranului s-a oprit"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml
deleted file mode 100644
index e23558c..0000000
--- a/packages/SystemUI/res/values-ru/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-подключение установлено"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-подключение отключено"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Через приложение <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Уведомления"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Уведомлений нет."</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Выполняется запись с микрофона"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Выполняется запись с камеры"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Выполняется запись с камеры и микрофона"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Запись с микрофона остановлена"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Запись с камеры остановлена"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Запись с камеры и микрофона остановлена"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Запись видео с экрана началась."</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Запись видео с экрана остановлена."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-si/strings_tv.xml b/packages/SystemUI/res/values-si/strings_tv.xml
deleted file mode 100644
index 1eaa24a..0000000
--- a/packages/SystemUI/res/values-si/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN සම්බන්ධිතයි"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN විසන්ධි කර ඇත"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> හරහා"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"දැනුම්දීම්"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"දැනුම්දීම් නැත"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"මයික්‍රෆෝනය පටිගත කරයි"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"කැමරාව පටිගත කරයි"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"කැමරාව සහ මයික්‍රෆෝනය පටිගත කරයි"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"මයික්‍රෆෝනය පටිගත කිරීම නැවැත්වීය"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"කැමරාව පටිගත කිරීම නැවැත්වීය"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"කැමරාව සහ මයික්‍රෆෝනය පටිගත කිරීම නැවැත්වීය"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"තිර පටිගත කිරීම ආරම්භ විය"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"තිර පටිගත කිරීම නතර විය"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sk/strings_tv.xml b/packages/SystemUI/res/values-sk/strings_tv.xml
deleted file mode 100644
index a7479aa..0000000
--- a/packages/SystemUI/res/values-sk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Sieť VPN je pripojená"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Sieť VPN je odpojená"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Cez: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Upozornenia"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Žiadne upozornenia"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofón nahráva"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera nahráva"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera a mikrofón nahrávajú"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofón prestal nahrávať"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera prestala nahrávať"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera a mikrofón prestali nahrávať"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Nahrávanie obrazovky bolo spustené"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Nahrávanie obrazovky bolo zastavené"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml
deleted file mode 100644
index 765b590..0000000
--- a/packages/SystemUI/res/values-sl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Povezava z omrežjem VPN je vzpostavljena"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Povezava z omrežjem VPN je prekinjena"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prek storitve <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obvestila"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ni obvestil"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Snemanje z mikrofonom"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Snemanje s fotoaparatom"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Snemanje s fotoaparatom in mikrofonom"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Ustavljeno snemanje z mikrofonom"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Ustavljeno snemanje s fotoaparatom"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Ustavljeno snemanje s fotoaparatom in mikrofonom"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Snemanje zaslona se je začelo."</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Snemanje zaslona je ustavljeno."</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sq/strings_tv.xml b/packages/SystemUI/res/values-sq/strings_tv.xml
deleted file mode 100644
index 12b42b8..0000000
--- a/packages/SystemUI/res/values-sq/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-ja është e lidhur"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-ja është shkëputur"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Nëpërmjet <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Njoftimet"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Asnjë njoftim"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"\"Mikrofoni\" po regjistron"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"\"Kamera\" po regjistron"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"\"Kamera\" dhe \"Mikrofoni\" po regjistrojnë"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"\"Mikrofoni\" ndaloi së regjistruari"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"\"Kamera\" ndaloi së regjistruari"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"\"Kamera\" dhe \"Mikrofoni\" ndaluan së regjistruari"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Regjistrimi i ekranit filloi"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Regjistrimi i ekranit ndaloi"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sr/strings_tv.xml b/packages/SystemUI/res/values-sr/strings_tv.xml
deleted file mode 100644
index 85c38ca..0000000
--- a/packages/SystemUI/res/values-sr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN је повезан"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Веза са VPN-ом је прекинута"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Преко: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Обавештења"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Нема обавештења"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Микрофон снима"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера снима"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера и микрофон снимају"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Снимање микрофоном је заустављено"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Снимање камером је заустављено"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Снимање камером и микрофоном је заустављено"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Снимање екрана је започето"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Снимање екрана је заустављено"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml
deleted file mode 100644
index 6830436..0000000
--- a/packages/SystemUI/res/values-sv/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN är anslutet"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN är frånkopplat"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Aviseringar"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Inga aviseringar"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofonen spelar in"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kameran spelar in"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kameran och mikrofonen spelar in"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofonen slutade spela in"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kameran slutade spela in"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kameran och mikrofonen slutade spelade in"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Skärminspelningen har startats"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Skärminspelningen har stoppats"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-sw/strings_tv.xml b/packages/SystemUI/res/values-sw/strings_tv.xml
deleted file mode 100644
index f3e2ca2..0000000
--- a/packages/SystemUI/res/values-sw/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN imeunganishwa"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN imeondolewa"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Kupitia <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Arifa"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Hakuna Arifa"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Maikrofoni inarekodi"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera inarekodi"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera na Maikrofoni zinarekodi"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Maikrofoni imeacha kurekodi"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera imeacha kurekodi"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera na Maikrofoni zimeacha kurekodi"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Imeanza kurekodi skrini"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Imeacha kurekodi skrini"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ta/strings_tv.xml b/packages/SystemUI/res/values-ta/strings_tv.xml
deleted file mode 100644
index 3e0baf6..0000000
--- a/packages/SystemUI/res/values-ta/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN இணைக்கப்பட்டது"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN துண்டிக்கப்பட்டது"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> வழியாக"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"அறிவிப்புகள்"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"அறிவிப்புகள் எதுவுமில்லை"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"மைக்ரோஃபோன் ரெக்கார்டு செய்கிறது"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"கேமரா ரெக்கார்டு செய்கிறது"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"கேமராவும் மைக்ரோஃபோனும் ரெக்கார்டு செய்கின்றன"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"மைக்ரோஃபோன் ரெக்கார்டு செய்வதை நிறுத்திவிட்டது"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"கேமரா ரெக்கார்டு செய்வதை நிறுத்திவிட்டது"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"கேமராவும் மைக்ரோஃபோனும் ரெக்கார்டு செய்வதை நிறுத்திவிட்டன"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"ஸ்கிரீன் ரெக்கார்டிங் தொடங்கியது"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"ஸ்கிரீன் ரெக்கார்டிங் நிறுத்தப்பட்டது"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-te/strings_tv.xml b/packages/SystemUI/res/values-te/strings_tv.xml
deleted file mode 100644
index 2131064..0000000
--- a/packages/SystemUI/res/values-te/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN కనెక్ట్ అయింది"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN డిస్‌కనెక్ట్ అయింది"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> ద్వారా"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"నోటిఫికేషన్‌లు"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"నోటిఫికేషన్‌లు లేవు"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"మైక్రోఫోన్ రికార్డింగ్ చేస్తోంది"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"కెమెరా రికార్డింగ్ చేస్తోంది"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"కెమెరా, మైక్రోఫోన్‌లు రికార్డింగ్ చేస్తున్నాయి"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"మైక్రోఫోన్ రికార్డింగ్ చేయడం ఆపివేసింది"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"రికార్డింగ్ చేయడాన్ని కెమెరా ఆపివేసింది"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"కెమెరా, మైక్రోఫోన్‌లు రికార్డింగ్ చేయడం ఆపివేశాయి"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"స్క్రీన్ రికార్డింగ్ ప్రారంభమైంది"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"స్క్రీన్ రికార్డింగ్ ఆపివేయబడింది"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-television/colors.xml b/packages/SystemUI/res/values-television/colors.xml
deleted file mode 100644
index 3e9e182..0000000
--- a/packages/SystemUI/res/values-television/colors.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2021, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources>
-    <color name="volume_dialog_background_color">#E61F232B</color>
-    <color name="volume_dialog_background_color_above_blur">#C71F232B</color>
-
-    <color name="bottom_sheet_icon_color">#D2E3FC</color>
-
-    <color name="bottom_sheet_title_color">#E8F0FE</color>
-    <color name="bottom_sheet_body_color">#D2E3FC</color>
-
-    <color name="bottom_sheet_background_color">#1F232C</color>
-    <color name="bottom_sheet_background_color_with_blur">#AA1A2734</color>
-
-    <color name="bottom_sheet_button_background_color_focused">#E8F0FE</color>
-    <color name="bottom_sheet_button_background_color_unfocused">#0FE8EAED</color>
-
-    <color name="bottom_sheet_button_text_color_focused">#DB202124</color>
-    <color name="bottom_sheet_button_text_color_unfocused">#B5E8EAED</color>
-
-    <color name="privacy_mic_cam_chip">#5BB974</color> <!-- g400 -->
-    <color name="privacy_media_projection_chip">#C9CCD0</color>
-    <color name="privacy_icon_tint">#30302A</color>
-    <color name="privacy_chip_dot_bg_tint">#66000000</color>
-    <color name="cast_connected_fill">#FF0000</color>
-</resources>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
deleted file mode 100644
index 2ace86f..0000000
--- a/packages/SystemUI/res/values-television/config.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2019, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<!-- These resources are around just to allow their values to be customized
-     for different hardware and product builds. -->
-<resources>
-    <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. -->
-    <integer name="recents_svelte_level">3</integer>
-
-    <!-- Show a separate icon for low and high volume on the volume dialog -->
-    <bool name="config_showLowMediaVolumeIcon">true</bool>
-
-    <!-- Change the volume row tint when it is inactive, i.e. when it is being dismissed -->
-    <bool name="config_changeVolumeRowTintWhenInactive">false</bool>
-
-    <!-- The duraction of the show animation for the volume dialog in milliseconds -->
-    <integer name="config_dialogShowAnimationDurationMs">600</integer>
-
-    <!-- The duraction of the hide animation for the volume dialog in milliseconds -->
-    <integer name="config_dialogHideAnimationDurationMs">400</integer>
-
-    <!-- Whether to use window background blur for the volume dialog. -->
-    <bool name="config_volumeDialogUseBackgroundBlur">true</bool>
-
-    <!-- Whether to tint the icon of the sensor hardware privacy toggle unblock dialog.
-        Set to false if using a custom icon. -->
-    <bool name="config_unblockHwSensorIconEnableTint">true</bool>
-
-    <!-- Configuration to set Learn more in device logs as URL link -->
-    <bool name="log_access_confirmation_learn_more_as_link">false</bool>
-</resources>
diff --git a/packages/SystemUI/res/values-television/dimens.xml b/packages/SystemUI/res/values-television/dimens.xml
deleted file mode 100644
index ee615d9..0000000
--- a/packages/SystemUI/res/values-television/dimens.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<resources>
-    <!-- Opacity at which the background for the shutdown UI will be drawn. -->
-    <item name="shutdown_scrim_behind_alpha" format="float" type="dimen">1.0</item>
-
-    <dimen name="bottom_sheet_padding_horizontal">32dp</dimen>
-    <dimen name="bottom_sheet_padding_vertical">24dp</dimen>
-
-    <dimen name="bottom_sheet_icon_size">42dp</dimen>
-    <dimen name="bottom_sheet_icon_margin">8dp</dimen>
-    <dimen name="bottom_sheet_title_margin_bottom">18dp</dimen>
-    <dimen name="bottom_sheet_details_margin_bottom">8dp</dimen>
-
-    <dimen name="bottom_sheet_actions_width">296dp</dimen>
-    <dimen name="bottom_sheet_actions_spacing">12dp</dimen>
-    <item name="bottom_sheet_button_selection_scaled" format="float" type="dimen">1.1</item>
-    <dimen name="bottom_sheet_button_width">232dp</dimen>
-    <dimen name="bottom_sheet_button_padding_horizontal">20dp</dimen>
-    <dimen name="bottom_sheet_button_padding_vertical">16dp</dimen>
-
-    <dimen name="bottom_sheet_corner_radius">24dp</dimen>
-    <dimen name="bottom_sheet_button_corner_radius">10dp</dimen>
-
-    <dimen name="bottom_sheet_min_height">208dp</dimen>
-    <dimen name="bottom_sheet_margin">24dp</dimen>
-    <dimen name="bottom_sheet_background_blur_radius">37dp</dimen>
-
-    <dimen name="privacy_chips_max_width">110dp</dimen>
-    <dimen name="privacy_chips_bar_padding">9dp</dimen>
-    <dimen name="privacy_chip_margin">3dp</dimen>
-    <dimen name="privacy_chip_icon_margin_in_between">4dp</dimen>
-    <dimen name="privacy_chip_padding_horizontal">5dp</dimen>
-    <dimen name="privacy_chip_icon_size">12dp</dimen>
-    <dimen name="privacy_chip_collapsed_icon_size">10dp</dimen>
-    <dimen name="privacy_chip_height">24dp</dimen>
-    <dimen name="privacy_chip_radius">12dp</dimen>
-
-    <dimen name="privacy_chip_dot_size">8dp</dimen>
-    <dimen name="privacy_chip_dot_radius">4dp</dimen>
-
-    <dimen name="privacy_chip_dot_bg_width">24dp</dimen>
-    <dimen name="privacy_chip_dot_bg_height">18dp</dimen>
-    <dimen name="privacy_chip_dot_bg_radius">9dp</dimen>
-
-    <dimen name="unblock_hw_sensor_icon_width">@dimen/bottom_sheet_icon_size</dimen>
-    <dimen name="unblock_hw_sensor_icon_height">@dimen/bottom_sheet_icon_size</dimen>
-
-</resources>
diff --git a/packages/SystemUI/res/values-television/integers.xml b/packages/SystemUI/res/values-television/integers.xml
deleted file mode 100644
index 02f5d0d..0000000
--- a/packages/SystemUI/res/values-television/integers.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-<resources>
-    <!-- The position of the volume dialog on the screen.
-         See com.android.systemui.volume.VolumeDialogImpl.
-         Value 81 corresponds to BOTTOM|CENTER_HORIZONTAL.
-         Value 21 corresponds to RIGHT|CENTER_VERTICAL.
-         Value 8388629 corresponds to END|CENTER_VERTICAL -->
-    <integer name="volume_dialog_gravity">8388629</integer>
-
-    <integer name="privacy_chip_animation_millis">300</integer>
-</resources>
diff --git a/packages/SystemUI/res/values-television/styles.xml b/packages/SystemUI/res/values-television/styles.xml
index c517845..4a4fac2 100644
--- a/packages/SystemUI/res/values-television/styles.xml
+++ b/packages/SystemUI/res/values-television/styles.xml
@@ -14,15 +14,9 @@
      limitations under the License.
 -->
 
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-    <style name="Theme.SystemUI.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog" />
-    <style name="Theme.SystemUI.Dialog.Alert" parent="@*android:style/Theme.DeviceDefault.Dialog.Alert" />
-
-    <style name="Animation.ShutdownUi">
-        <item name="android:windowEnterAnimation">@null</item>
-        <item name="android:windowExitAnimation">@null</item>
-    </style>
 
     <style name="volume_dialog_theme" parent="Theme.SystemUI">
         <item name="android:colorAccent">@color/tv_volume_dialog_accent</item>
@@ -30,43 +24,4 @@
         <item name="android:dialogCornerRadius">@dimen/volume_dialog_panel_width_half</item>
     </style>
 
-    <style name="PrivacyChip">
-        <item name="android:colorError">@color/cast_connected_fill</item>
-    </style>
-
-    <style name="BottomSheet" parent="Theme.Leanback">
-        <item name="android:windowIsFloating">true</item>
-        <item name="android:windowActivityTransitions">true</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:backgroundDimAmount">0.2</item>
-    </style>
-
-    <style name="BottomSheet.TitleText">
-        <item name="android:textSize">28sp</item>
-        <item name="android:textColor">@color/bottom_sheet_title_color</item>
-    </style>
-
-    <style name="BottomSheet.BodyText">
-        <item name="android:textSize">16sp</item>
-        <item name="android:textColor">@color/bottom_sheet_body_color</item>
-    </style>
-
-    <style name="BottomSheet.ActionItem">
-        <item name="android:layout_width">@dimen/bottom_sheet_button_width</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:gravity">left|center_vertical</item>
-        <item name="android:textSize">16sp</item>
-        <item name="android:textColor">@color/bottom_sheet_button_text_color</item>
-        <item name="android:background">@drawable/bottom_sheet_button_background</item>
-        <item name="android:paddingHorizontal">@dimen/bottom_sheet_button_padding_horizontal</item>
-        <item name="android:paddingVertical">@dimen/bottom_sheet_button_padding_vertical</item>
-        <item name="android:stateListAnimator">@anim/tv_bottom_sheet_button_state_list_animator</item>
-    </style>
-
-    <!-- The style for log access consent button -->
-    <style name="LogAccessDialogTheme" parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
-        <item name="permissionGrantButtonTopStyle">?android:buttonBarButtonStyle</item>
-        <item name="permissionGrantButtonBottomStyle">?android:buttonBarButtonStyle</item>
-    </style>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings_tv.xml b/packages/SystemUI/res/values-th/strings_tv.xml
deleted file mode 100644
index 1df2612..0000000
--- a/packages/SystemUI/res/values-th/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"เชื่อมต่อ VPN แล้ว"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"ยกเลิกการเชื่อมต่อ VPN แล้ว"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"ผ่าน <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"การแจ้งเตือน"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"ไม่มีการแจ้งเตือน"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"ไมโครโฟนกำลังบันทึก"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"กล้องกำลังบันทึก"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"กล้องและไมโครโฟนกำลังบันทึก"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"ไมโครโฟนหยุดบันทึกแล้ว"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"กล้องหยุดบันทึกแล้ว"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"กล้องและไมโครโฟนหยุดบันทึกแล้ว"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"เริ่มบันทึกหน้าจอแล้ว"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"หยุดการบันทึกหน้าจอแล้ว"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-tl/strings_tv.xml b/packages/SystemUI/res/values-tl/strings_tv.xml
deleted file mode 100644
index 890ccd4..0000000
--- a/packages/SystemUI/res/values-tl/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Nakakonekta ang VPN"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Nakadiskonekta ang VPN"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Sa pamamagitan ng <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Mga Notification"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Walang Notification"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Nagre-record ang Mikropono"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Nagre-record ang Camera"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Nagre-record ang Camera at Mikropono"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Huminto sa pag-record ang Mikropono"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Huminto sa pag-record ang Camera"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Huminto sa pag-record ang Camera at Mikropono"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Sinimulan ang pag-record ng screen"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Itinigil ang pag-record ng screen"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml
deleted file mode 100644
index f981aa2..0000000
--- a/packages/SystemUI/res/values-tr/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN bağlandı"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN bağlantısı kesildi"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> üzerinden"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirimler"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildirim Yok"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon kaydediyor"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera kaydediyor"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera ve Mikrofon kaydediyor"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon kaydı durdu"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera kaydı durdu"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera ve Mikrofon kaydı durdu"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekran kaydı başladı"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekran kaydı durduruldu"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-uk/strings_tv.xml b/packages/SystemUI/res/values-uk/strings_tv.xml
deleted file mode 100644
index 8b96aac..0000000
--- a/packages/SystemUI/res/values-uk/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Мережу VPN під\'єднано"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Мережу VPN від\'єднано"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Через <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Сповіщення"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Немає сповіщень"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Мікрофон записує"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Камера записує"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Камера й мікрофон записують"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Мікрофон припинив запис"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Камера припинила запис"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Камера й мікрофон припинили запис"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Запис відео з екрана розпочато"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Запис відео з екрана зупинено"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-ur/strings_tv.xml b/packages/SystemUI/res/values-ur/strings_tv.xml
deleted file mode 100644
index ed186ac..0000000
--- a/packages/SystemUI/res/values-ur/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"‏VPN منسلک ہے"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‏VPN غیر منسلک ہے"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"بذریعہ <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"اطلاعات"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"کوئی اطلاع نہیں ہے"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"مائیکروفون ریکارڈ کر رہا ہے"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"کیمرا ریکارڈ کر رہا ہے"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"کیمرا اور مائیکروفون ریکارڈ کر رہا ہے"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"مائیکروفون نے ریکارڈ کرنا بند کر دیا"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"کیمرے نے ریکارڈ کرنا بند کر دیا"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"کیمرے اور مائیکروفون نے ریکارڈ کرنا بند کر دیا"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"اسکرین ریکارڈنگ شروع ہو گئی"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"اسکرین ریکارڈنگ بند کر دی گئی"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-uz/strings_tv.xml b/packages/SystemUI/res/values-uz/strings_tv.xml
deleted file mode 100644
index c34ee4f..0000000
--- a/packages/SystemUI/res/values-uz/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN ulandi"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN uzildi"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> orqali"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirishnomalar"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildirishnomalar yoʻq"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Mikrofon yozib olmoqda"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Kamera yozib olmoqda"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Kamera va mikrofon yozib olmoqda"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Mikrofon yozib olishni toʻxtatdi"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Kamera yozib olishni toʻxtatdi"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Kamera va mikrofon yozib olishni toʻxtatdi"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ekrandan yozib olish boshlandi"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ekrandan yozib olish toʻxtatildi"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-vi/strings_tv.xml b/packages/SystemUI/res/values-vi/strings_tv.xml
deleted file mode 100644
index b140fc0..0000000
--- a/packages/SystemUI/res/values-vi/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN đã được kết nối"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN đã bị ngắt kết nối"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Thông qua <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Thông báo"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Không có thông báo"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Micrô đang ghi"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Máy ảnh đang ghi"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Máy ảnh và micrô đang ghi"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Micrô đã dừng ghi"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Máy ảnh đã dừng ghi"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Máy ảnh và micrô đã dừng ghi"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Đã bắt đầu ghi màn hình"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Đã dừng ghi màn hình"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml b/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
deleted file mode 100644
index ad4d94b0..0000000
--- a/packages/SystemUI/res/values-zh-rCN/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN 已连接"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 已断开连接"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"通过“<xliff:g id="VPN_APP">%1$s</xliff:g>”"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"没有通知"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"麦克风正在录制"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"相机正在录制"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"相机和麦克风正在录制"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"麦克风已停止录制"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"相机已停止录制"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"相机和麦克风已停止录制"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"已开始录制屏幕"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"已停止录制屏幕"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml b/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
deleted file mode 100644
index 88ec5e0..0000000
--- a/packages/SystemUI/res/values-zh-rHK/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN 已連線"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 已中斷連線"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"透過 <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"沒有通知"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"麥克風正在錄音"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"相機正在錄影"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"相機和麥克風正在錄影及錄音"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"麥克風已停止錄音"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"相機已停止錄影"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"相機和麥克風已停止錄影及錄音"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"已開始錄製螢幕畫面"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"已停止錄製螢幕畫面"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml b/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
deleted file mode 100644
index b6b1b1e..0000000
--- a/packages/SystemUI/res/values-zh-rTW/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN 已連線"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN 連線已中斷"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"透過「<xliff:g id="VPN_APP">%1$s</xliff:g>」"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"通知"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"沒有通知"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"麥克風正在錄音"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"相機正在錄影"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"相機和麥克風正在錄影及錄音"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"麥克風已停止錄音"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"相機已停止錄影"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"相機和麥克風已停止錄影及錄音"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"已開始錄製螢幕畫面"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"已停止錄製螢幕畫面"</string>
-</resources>
diff --git a/packages/SystemUI/res/values-zu/strings_tv.xml b/packages/SystemUI/res/values-zu/strings_tv.xml
deleted file mode 100644
index 59fcb8d..0000000
--- a/packages/SystemUI/res/values-zu/strings_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"I-VPN ixhunyiwe"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"I-VPN inqanyuliwe"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Nge-<xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
-    <string name="tv_notification_panel_title" msgid="5311050946506276154">"Izaziso"</string>
-    <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Azikho Izaziso"</string>
-    <string name="mic_recording_announcement" msgid="7587123608060316575">"Imakrofoni iyarekhoda"</string>
-    <string name="camera_recording_announcement" msgid="7240177719403759112">"Ikhamera iyarekhoda"</string>
-    <string name="mic_and_camera_recording_announcement" msgid="8599231390508812667">"Ikhamera nemakrofoni kuyarekhoda"</string>
-    <string name="mic_stopped_recording_announcement" msgid="7301537004900721242">"Imakrofoni iyekile ukurekhoda"</string>
-    <string name="camera_stopped_recording_announcement" msgid="8540496432367032801">"Ikhamera iyeke ukurekhoda"</string>
-    <string name="mic_camera_stopped_recording_announcement" msgid="8708524579599977412">"Ikhemera nemakrofoni kuyekile ukurekhoda"</string>
-    <string name="screen_recording_announcement" msgid="2996750593472241520">"Ukurekhoda isikrini kuqalile"</string>
-    <string name="screen_stopped_recording_announcement" msgid="979749439036681416">"Ukurekhoda isikrini kumisiwe"</string>
-</resources>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 51b57bd..2bab3cb 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 -->
+<!-- TODO(b/289498394) move this to the TvSystemUI target -->
 <resources>
     <color name="recents_tv_card_background_color">#FF263238</color>
     <color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
@@ -32,8 +33,4 @@
     <color name="tv_volume_dialog_seek_bar_background">#A03C4043</color>
     <color name="tv_volume_dialog_seek_bar_fill">#FFF8F9FA</color>
     <color name="tv_volume_dialog_accent">#FFDADCE0</color>
-
-    <color name="tv_notification_default_background_color">#383838</color>
-    <color name="tv_notification_blur_background_color">#a0383838</color>
-    <color name="tv_notification_text_color">#FFFFFF</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9c864ab..a38c629 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -366,10 +366,16 @@
 
     <!-- Whether or not notifications that can be expanded will always be in their expanded state.
          This value only affects notifications that are not a group of notifications from the same
-         applications. If this value is false, then only the first notification will be expanded;
-         the other notifications need to be manually expanded by the user. -->
+         applications. If this value is false, then only the first notification will be expanded
+         when config_autoExpandFirstNotification is true; the other notifications need to be
+         manually expanded by the user. -->
     <bool name="config_alwaysExpandNonGroupedNotifications">false</bool>
 
+    <!-- Whether or not the first expandable notification will be expanded automatically by the
+         system. This value only affects notifications that are not a group of notifications from
+         the same applications and when config_alwaysExpandNonGroupedNotifications is false. -->
+    <bool name="config_autoExpandFirstNotification">true</bool>
+
     <!-- Whether or not an expandable notification can be manually expanded or collapsed by the
          user. Grouped notifications are still expandable even if this value is false. -->
     <bool name="config_enableNonGroupedNotificationExpand">true</bool>
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
deleted file mode 100644
index 8e372e4..0000000
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Title and subtitle for AudioRecordingIndicator -->
-
-    <string name="notification_vpn_connected">VPN is connected</string>
-    <string name="notification_vpn_disconnected">VPN is disconnected</string>
-    <!-- Disclosure text in the connected notification that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=40] -->
-    <string name="notification_disclosure_vpn_text">Via <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
-
-    <string name="tv_notification_panel_title">Notifications</string>
-    <string name="tv_notification_panel_no_notifications">No Notifications</string>
-
-    <string name="mic_recording_announcement">Microphone is recording</string>
-    <string name="camera_recording_announcement">Camera is recording</string>
-    <string name="mic_and_camera_recording_announcement">Camera and Microphone are recording</string>
-    <string name="mic_stopped_recording_announcement">Microphone stopped recording</string>
-    <string name="camera_stopped_recording_announcement">Camera stopped recording</string>
-    <string name="mic_camera_stopped_recording_announcement">Camera and Microphone stopped recording</string>
-    <string name="screen_recording_announcement">Screen recording started</string>
-    <string name="screen_stopped_recording_announcement">Screen recording stopped</string>
-
-</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6b85621..31f40e9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1126,13 +1126,13 @@
 
     <style name="Widget.Dialog.Button.BorderButton">
         <item name="android:background">@drawable/qs_dialog_btn_outline</item>
-        <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+        <item name="android:textColor">@color/qs_dialog_btn_outline_text</item>
     </style>
 
     <style name="Widget.Dialog.Button.Large">
         <item name="android:background">@drawable/qs_dialog_btn_filled_large</item>
         <item name="android:minHeight">56dp</item>
-        <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryFixed</item>
+        <item name="android:textColor">@color/qs_dialog_btn_filled_large_text</item>
     </style>
 
     <style name="Widget.Dialog.Button.QuickSettings">
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
deleted file mode 100644
index 3e09026..0000000
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/**
- * Copyright (c) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="PipTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:backgroundDimEnabled">false</item>
-        <item name="android:windowDisablePreview">true</item>
-     </style>
-
-    <style name="TvSidePanelTheme">
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:backgroundDimEnabled">false</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowContentOverlay">@null</item>
-        <item name="android:windowIsFloating">true</item>
-    </style>
-</resources>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 28e786b..ca30e15 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -65,7 +65,7 @@
     ],
     min_sdk_version: "current",
     plugins: ["dagger2-compiler"],
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
 }
 
 java_library {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
index 50e5466..1cb8e43 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt
@@ -12,12 +12,9 @@
 ) : FrameLayout(context, attrs) {
     private var drawAlpha: Int = 255
 
-    init {
-        setLayerType(View.LAYER_TYPE_SOFTWARE, null)
-    }
-
     protected override fun onSetAlpha(alpha: Int): Boolean {
-        drawAlpha = alpha
+        // Ignore alpha passed from View, prefer to compute it from set values
+        drawAlpha = (255 * this.alpha * transitionAlpha).toInt()
         return true
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 2377057..d9b7bde 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -69,7 +69,7 @@
                 (long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED),
                 0.6f /* translationScale */,
                 0.45f /* delayScale */, AnimationUtils.loadInterpolator(
-                        mContext, android.R.interpolator.fast_out_linear_in));
+                       mContext, android.R.interpolator.fast_out_linear_in));
         mDisappearYTranslation = getResources().getDimensionPixelSize(
                 R.dimen.disappear_y_translation);
         mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
@@ -82,8 +82,10 @@
     }
 
     void onDevicePostureChanged(@DevicePostureInt int posture) {
-        mLastDevicePosture = posture;
-        updateMargins();
+        if (mLastDevicePosture != posture) {
+            mLastDevicePosture = posture;
+            updateMargins();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 38c07dc..2bdf46e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -104,8 +104,10 @@
     }
 
     void onDevicePostureChanged(@DevicePostureInt int posture) {
-        mLastDevicePosture = posture;
-        updateMargins();
+        if (mLastDevicePosture != posture) {
+            mLastDevicePosture = posture;
+            updateMargins();
+        }
     }
 
     private void updateMargins() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index a6252a3..7585279 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -113,6 +113,7 @@
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardStatusView:");
         pw.println("  mDarkAmount: " + mDarkAmount);
+        pw.println("  visibility: " + getVisibility());
         if (mClockView != null) {
             mClockView.dump(pw, args);
         }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 6854c97..a04d13b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -37,15 +37,19 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.viewpager.widget.ViewPager;
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.keyguard.KeyguardClockSwitch.ClockSize;
 import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.Dumpable;
 import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ClockController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
@@ -58,14 +62,17 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ViewController;
 
+import java.io.PrintWriter;
+
 import javax.inject.Inject;
 
 /**
  * Injectable controller for {@link KeyguardStatusView}.
  */
-public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> {
+public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> implements
+        Dumpable {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
-    private static final String TAG = "KeyguardStatusViewController";
+    @VisibleForTesting static final String TAG = "KeyguardStatusViewController";
 
     /**
      * Duration to use for the animator when the keyguard status view alignment changes, and a
@@ -87,6 +94,8 @@
 
     private Boolean mStatusViewCentered = true;
 
+    private DumpManager mDumpManager;
+
     private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener =
             new TransitionListenerAdapter() {
                 @Override
@@ -112,7 +121,8 @@
             ScreenOffAnimationController screenOffAnimationController,
             KeyguardLogger logger,
             FeatureFlags featureFlags,
-            InteractionJankMonitor interactionJankMonitor) {
+            InteractionJankMonitor interactionJankMonitor,
+            DumpManager dumpManager) {
         super(keyguardStatusView);
         mKeyguardSliceViewController = keyguardSliceViewController;
         mKeyguardClockSwitchController = keyguardClockSwitchController;
@@ -123,11 +133,13 @@
                 logger.getBuffer());
         mInteractionJankMonitor = interactionJankMonitor;
         mFeatureFlags = featureFlags;
+        mDumpManager = dumpManager;
     }
 
     @Override
     public void onInit() {
         mKeyguardClockSwitchController.init();
+        mDumpManager.registerDumpable(this);
     }
 
     @Override
@@ -143,6 +155,13 @@
     }
 
     /**
+     * Called in notificationPanelViewController to avoid leak
+     */
+    public void onDestroy() {
+        mDumpManager.unregisterDumpable(TAG);
+    }
+
+    /**
      * Updates views on doze time tick.
      */
     public void dozeTimeTick() {
@@ -365,6 +384,19 @@
             // Excluding media from the transition on split-shade, as it doesn't transition
             // horizontally properly.
             transition.excludeTarget(R.id.status_view_media_container, true);
+
+            // Exclude smartspace viewpager and its children from the transition.
+            //     - Each step of the transition causes the ViewPager to invoke resize,
+            //     which invokes scrolling to the recalculated position. The scrolling
+            //     actions are congested, resulting in kinky translation, and
+            //     delay in settling to the final position. (http://b/281620564#comment1)
+            //     - Also, the scrolling is unnecessary in the transition. We just want
+            //     the viewpager to stay on the same page.
+            //     - Exclude by Class type instead of resource id, since the resource id
+            //     isn't available for all devices, and probably better to exclude all
+            //     ViewPagers any way.
+            transition.excludeTarget(ViewPager.class, true);
+            transition.excludeChildren(ViewPager.class, true);
         }
 
         transition.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
@@ -397,6 +429,24 @@
                 adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION);
                 adapter.addTarget(clockView);
                 set.addTransition(adapter);
+
+                if (splitShadeEnabled) {
+                    // Exclude smartspace viewpager and its children from the transition set.
+                    //     - This is necessary in addition to excluding them from the
+                    //     ChangeBounds child transition.
+                    //     - Without this, the viewpager is scrolled to the new position
+                    //     (corresponding to its end size) before the size change is realized.
+                    //     Note that the size change is realized at the end of the ChangeBounds
+                    //     transition. With the "prescrolling", the viewpager ends up in a weird
+                    //     position, then recovers smoothly during the transition, and ends at
+                    //     the position for the current page.
+                    //     - Exclude by Class type instead of resource id, since the resource id
+                    //     isn't available for all devices, and probably better to exclude all
+                    //     ViewPagers any way.
+                    set.excludeTarget(ViewPager.class, true);
+                    set.excludeChildren(ViewPager.class, true);
+                }
+
                 set.addListener(mKeyguardStatusAlignmentTransitionListener);
                 TransitionManager.beginDelayedTransition(notifContainerParent, set);
             }
@@ -408,6 +458,11 @@
         constraintSet.applyTo(notifContainerParent);
     }
 
+    @Override
+    public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+        mView.dump(pw, args);
+    }
+
     @VisibleForTesting
     static class SplitShadeTransitionAdapter extends Transition {
         private static final String PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft";
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5e84a57..6bd9df0 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -164,13 +164,13 @@
 import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.DetectionStatus;
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus;
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus;
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus;
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus;
 import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions;
 import com.android.systemui.log.SessionTracker;
 import com.android.systemui.plugins.WeatherData;
@@ -1471,28 +1471,32 @@
     private FaceAuthenticationListener mFaceAuthenticationListener =
             new FaceAuthenticationListener() {
                 @Override
-                public void onAuthenticationStatusChanged(@NonNull AuthenticationStatus status) {
-                    if (status instanceof AcquiredAuthenticationStatus) {
+                public void onAuthenticationStatusChanged(
+                        @NonNull FaceAuthenticationStatus status
+                ) {
+                    if (status instanceof AcquiredFaceAuthenticationStatus) {
                         handleFaceAcquired(
-                                ((AcquiredAuthenticationStatus) status).getAcquiredInfo());
-                    } else if (status instanceof ErrorAuthenticationStatus) {
-                        ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status;
+                                ((AcquiredFaceAuthenticationStatus) status).getAcquiredInfo());
+                    } else if (status instanceof ErrorFaceAuthenticationStatus) {
+                        ErrorFaceAuthenticationStatus error =
+                                (ErrorFaceAuthenticationStatus) status;
                         handleFaceError(error.getMsgId(), error.getMsg());
-                    } else if (status instanceof FailedAuthenticationStatus) {
+                    } else if (status instanceof FailedFaceAuthenticationStatus) {
                         handleFaceAuthFailed();
-                    } else if (status instanceof HelpAuthenticationStatus) {
-                        HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status;
+                    } else if (status instanceof HelpFaceAuthenticationStatus) {
+                        HelpFaceAuthenticationStatus helpMsg =
+                                (HelpFaceAuthenticationStatus) status;
                         handleFaceHelp(helpMsg.getMsgId(), helpMsg.getMsg());
-                    } else if (status instanceof SuccessAuthenticationStatus) {
+                    } else if (status instanceof SuccessFaceAuthenticationStatus) {
                         FaceManager.AuthenticationResult result =
-                                ((SuccessAuthenticationStatus) status).getSuccessResult();
+                                ((SuccessFaceAuthenticationStatus) status).getSuccessResult();
                         handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
                     }
                 }
 
                 @Override
-                public void onDetectionStatusChanged(@NonNull DetectionStatus status) {
-                    handleFaceAuthenticated(status.getUserId(), status.isStrongBiometric());
+                public void onDetectionStatusChanged(@NonNull FaceDetectionStatus status) {
+                    handleBiometricDetected(status.getUserId(), FACE, status.isStrongBiometric());
                 }
             };
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 71f78c3..3990b10 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -31,6 +31,7 @@
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.Assert;
 
 import com.google.errorprone.annotations.CompileTimeConstant;
 
@@ -85,6 +86,7 @@
             boolean keyguardFadingAway,
             boolean goingToFullShade,
             int oldStatusBarState) {
+        Assert.isMainThread();
         PropertyAnimator.cancelAnimation(mView, AnimatableProperty.ALPHA);
         boolean isOccluded = mKeyguardStateController.isOccluded();
         mKeyguardViewVisibilityAnimating = false;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 04acd0b..b01e136 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -49,7 +49,6 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.hdmi.HdmiCecSetMenuLanguageHelper;
 import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -242,7 +241,6 @@
     @Inject Lazy<LocationController> mLocationController;
     @Inject Lazy<RotationLockController> mRotationLockController;
     @Inject Lazy<ZenModeController> mZenModeController;
-    @Inject Lazy<HdmiCecSetMenuLanguageHelper> mHdmiCecSetMenuLanguageHelper;
     @Inject Lazy<HotspotController> mHotspotController;
     @Inject Lazy<CastController> mCastController;
     @Inject Lazy<FlashlightController> mFlashlightController;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index e5a4d1a..7ae1443 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -479,10 +479,10 @@
                 failureReason,
                 messageAfterError = modalities.asDefaultHelpMessage(applicationContext),
                 authenticateAfterError = modalities.hasFingerprint,
-                suppressIf = { currentMessage ->
+                suppressIf = { currentMessage, history ->
                     modalities.hasFaceAndFingerprint &&
                         failedModality == BiometricModality.Face &&
-                        currentMessage.isError
+                        (currentMessage.isError || history.faceFailed)
                 },
                 failedModality = failedModality,
             )
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistory.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistory.kt
new file mode 100644
index 0000000..d002bf0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistory.kt
@@ -0,0 +1,46 @@
+/*
+ * 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.biometrics.ui.viewmodel
+
+import com.android.systemui.biometrics.shared.model.BiometricModality
+
+/** Contains metadata about key events that have occurred while biometric prompt is showing. */
+interface PromptHistory {
+
+    /** If face authentication has failed at least once. */
+    val faceFailed: Boolean
+
+    /** If fingerprint authentication has failed at least once. */
+    val fingerprintFailed: Boolean
+}
+
+class PromptHistoryImpl : PromptHistory {
+    private var failures = mutableSetOf<BiometricModality>()
+
+    override val faceFailed
+        get() = failures.contains(BiometricModality.Face)
+
+    override val fingerprintFailed
+        get() = failures.contains(BiometricModality.Fingerprint)
+
+    /** Record a failure event. */
+    fun failure(modality: BiometricModality) {
+        if (modality != BiometricModality.None) {
+            failures.add(modality)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 8a2e405..dca19c5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -204,6 +204,7 @@
             }
             .distinctUntilChanged()
 
+    private val history = PromptHistoryImpl()
     private var messageJob: Job? = null
 
     /**
@@ -214,13 +215,13 @@
      * is set (or via [showHelp] when not set) after the error is dismissed.
      *
      * The error is ignored if the user has already authenticated or if [suppressIf] is true given
-     * the currently showing [PromptMessage].
+     * the currently showing [PromptMessage] and [PromptHistory].
      */
     suspend fun showTemporaryError(
         message: String,
         messageAfterError: String,
         authenticateAfterError: Boolean,
-        suppressIf: (PromptMessage) -> Boolean = { false },
+        suppressIf: (PromptMessage, PromptHistory) -> Boolean = { _, _ -> false },
         hapticFeedback: Boolean = true,
         failedModality: BiometricModality = BiometricModality.None,
     ) = coroutineScope {
@@ -230,7 +231,9 @@
 
         _canTryAgainNow.value = supportsRetry(failedModality)
 
-        if (suppressIf(_message.value)) {
+        val suppress = suppressIf(_message.value, history)
+        history.failure(failedModality)
+        if (suppress) {
             return@coroutineScope
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 2abdb84..e3e9b3a 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
@@ -16,10 +16,10 @@
 
 package com.android.systemui.bouncer.domain.interactor
 
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.time.SystemClock
@@ -35,8 +35,8 @@
     private val keyguardStateController: KeyguardStateController,
     private val bouncerRepository: KeyguardBouncerRepository,
     private val biometricSettingsRepository: BiometricSettingsRepository,
-    private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
     private val systemClock: SystemClock,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
 ) {
     var receivedDownTouch = false
     val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
@@ -78,7 +78,7 @@
             biometricSettingsRepository.isFingerprintEnrolled.value &&
             biometricSettingsRepository.isStrongBiometricAllowed.value &&
             biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&
-            !deviceEntryFingerprintAuthRepository.isLockedOut.value &&
+            !keyguardUpdateMonitor.isFingerprintLockedOut &&
             !keyguardStateController.isUnlocked &&
             !statusBarStateController.isDozing
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index 8d5a2dd..32e40c9 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -20,7 +20,6 @@
 
 import com.android.systemui.ForegroundServicesDialog;
 import com.android.systemui.contrast.ContrastDialogActivity;
-import com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity;
 import com.android.systemui.keyguard.WorkLockActivity;
 import com.android.systemui.people.PeopleSpaceActivity;
 import com.android.systemui.people.widget.LaunchConversationActivity;
@@ -28,10 +27,7 @@
 import com.android.systemui.screenshot.appclips.AppClipsActivity;
 import com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity;
 import com.android.systemui.sensorprivacy.SensorUseStartedActivity;
-import com.android.systemui.sensorprivacy.television.TvSensorPrivacyChangedActivity;
-import com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity;
 import com.android.systemui.settings.brightness.BrightnessDialog;
-import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity;
 import com.android.systemui.telephony.ui.activity.SwitchToManagedProfileForCallActivity;
 import com.android.systemui.tuner.TunerActivity;
 import com.android.systemui.usb.UsbAccessoryUriActivity;
@@ -118,12 +114,6 @@
     @ClassKey(CreateUserActivity.class)
     public abstract Activity bindCreateUserActivity(CreateUserActivity activity);
 
-    /** Inject into TvNotificationPanelActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(TvNotificationPanelActivity.class)
-    public abstract Activity bindTvNotificationPanelActivity(TvNotificationPanelActivity activity);
-
     /** Inject into PeopleSpaceActivity. */
     @Binds
     @IntoMap
@@ -160,25 +150,7 @@
     @ClassKey(SensorUseStartedActivity.class)
     public abstract Activity bindSensorUseStartedActivity(SensorUseStartedActivity activity);
 
-    /** Inject into TvUnblockSensorActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(TvUnblockSensorActivity.class)
-    public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
 
-    /** Inject into HdmiCecSetMenuLanguageActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(HdmiCecSetMenuLanguageActivity.class)
-    public abstract Activity bindHdmiCecSetMenuLanguageActivity(
-            HdmiCecSetMenuLanguageActivity activity);
-
-    /** Inject into TvSensorPrivacyChangedActivity. */
-    @Binds
-    @IntoMap
-    @ClassKey(TvSensorPrivacyChangedActivity.class)
-    public abstract Activity bindTvSensorPrivacyChangedActivity(
-            TvSensorPrivacyChangedActivity activity);
 
     /** Inject into SwitchToManagedProfileForCallActivity. */
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 35cf4a1..3562477 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -43,8 +43,6 @@
 import com.android.systemui.screenshot.ReferenceScreenshotModule;
 import com.android.systemui.settings.dagger.MultiUserUtilsModule;
 import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeControllerImpl;
 import com.android.systemui.shade.ShadeExpansionStateManager;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyboardShortcutsModule;
@@ -150,9 +148,6 @@
     @Binds
     abstract DockManager bindDockManager(DockManagerImpl dockManager);
 
-    @Binds
-    abstract ShadeController provideShadeController(ShadeControllerImpl shadeController);
-
     @SysUISingleton
     @Provides
     @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index d82bf58..6fdb4ca 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -223,10 +223,10 @@
     Optional<NaturalRotationUnfoldProgressProvider> getNaturalRotationUnfoldProgressProvider();
 
     /** */
-    Optional<MediaMuteAwaitConnectionCli> getMediaMuteAwaitConnectionCli();
+    MediaMuteAwaitConnectionCli getMediaMuteAwaitConnectionCli();
 
     /** */
-    Optional<NearbyMediaDevicesManager> getNearbyMediaDevicesManager();
+    NearbyMediaDevicesManager getNearbyMediaDevicesManager();
 
     /**
      * Returns {@link CoreStartable}s that should be started with the application.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index d70c57f..dfec771 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -21,11 +21,9 @@
 import androidx.annotation.Nullable;
 
 import com.android.systemui.SystemUIInitializer;
-import com.android.systemui.tv.TvWMComponent;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.dagger.TvWMShellModule;
 import com.android.wm.shell.dagger.WMShellModule;
 import com.android.wm.shell.dagger.WMSingleton;
 import com.android.wm.shell.desktopmode.DesktopMode;
@@ -52,7 +50,7 @@
  * provided by its particular device/form-factor SystemUI implementation.
  *
  * ie. {@link WMComponent} includes {@link WMShellModule}
- *     and {@link TvWMComponent} includes {@link TvWMShellModule}
+ * and {@code TvWMComponent} includes {@link com.android.wm.shell.dagger.TvWMShellModule}
  */
 @WMSingleton
 @Subcomponent(modules = {WMShellModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
index 0e22406..f3a07fc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
@@ -16,15 +16,52 @@
 
 package com.android.systemui.dreams
 
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.core.LogLevel
-import com.android.systemui.log.dagger.DreamLog
-import javax.inject.Inject
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.core.MessageBuffer
 
 /** Logs dream-related stuff to a {@link LogBuffer}. */
-class DreamLogger @Inject constructor(@DreamLog private val buffer: LogBuffer) {
-    /** Logs a debug message to the buffer. */
-    fun d(tag: String, message: String) {
-        buffer.log(tag, LogLevel.DEBUG, { str1 = message }, { message })
-    }
+class DreamLogger(buffer: MessageBuffer, tag: String) : Logger(buffer, tag) {
+    fun logDreamOverlayEnabled(enabled: Boolean) =
+        d({ "Dream overlay enabled: $bool1" }) { bool1 = enabled }
+
+    fun logIgnoreAddComplication(reason: String, complication: String) =
+        d({ "Ignore adding complication, reason: $str1, complication: $str2" }) {
+            str1 = reason
+            str2 = complication
+        }
+
+    fun logIgnoreRemoveComplication(reason: String, complication: String) =
+        d({ "Ignore removing complication, reason: $str1, complication: $str2" }) {
+            str1 = reason
+            str2 = complication
+        }
+
+    fun logAddComplication(complication: String) =
+        d({ "Add dream complication: $str1" }) { str1 = complication }
+
+    fun logRemoveComplication(complication: String) =
+        d({ "Remove dream complication: $str1" }) { str1 = complication }
+
+    fun logOverlayActive(active: Boolean) = d({ "Dream overlay active: $bool1" }) { bool1 = active }
+
+    fun logLowLightActive(active: Boolean) =
+        d({ "Low light mode active: $bool1" }) { bool1 = active }
+
+    fun logHasAssistantAttention(hasAttention: Boolean) =
+        d({ "Dream overlay has Assistant attention: $bool1" }) { bool1 = hasAttention }
+
+    fun logStatusBarVisible(visible: Boolean) =
+        d({ "Dream overlay status bar visible: $bool1" }) { bool1 = visible }
+
+    fun logAvailableComplicationTypes(types: Int) =
+        d({ "Available complication types: $int1" }) { int1 = types }
+
+    fun logShouldShowComplications(showComplications: Boolean) =
+        d({ "Dream overlay should show complications: $bool1" }) { bool1 = showComplications }
+
+    fun logShowOrHideStatusBarItem(show: Boolean, type: String) =
+        d({ "${if (bool1) "Showing" else "Hiding"} dream status bar item: $int1" }) {
+            bool1 = show
+            str1 = type
+        }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
index 484bf3d..01fb522 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt
@@ -36,6 +36,9 @@
 import com.android.systemui.dreams.dagger.DreamOverlayModule
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
 import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.DreamLog
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.statusbar.CrossFadeHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
@@ -65,12 +68,14 @@
     private val mDreamInTranslationYDistance: Int,
     @Named(DreamOverlayModule.DREAM_IN_TRANSLATION_Y_DURATION)
     private val mDreamInTranslationYDurationMs: Long,
-    private val mLogger: DreamLogger,
+    @DreamLog logBuffer: LogBuffer,
 ) {
     companion object {
         private const val TAG = "DreamOverlayAnimationsController"
     }
 
+    private val logger = Logger(logBuffer, TAG)
+
     private var mAnimator: Animator? = null
     private lateinit var view: View
 
@@ -179,11 +184,11 @@
                 doOnEnd {
                     mAnimator = null
                     mOverlayStateController.setEntryAnimationsFinished(true)
-                    mLogger.d(TAG, "Dream overlay entry animations finished.")
+                    logger.d("Dream overlay entry animations finished.")
                 }
-                doOnCancel { mLogger.d(TAG, "Dream overlay entry animations canceled.") }
+                doOnCancel { logger.d("Dream overlay entry animations canceled.") }
                 start()
-                mLogger.d(TAG, "Dream overlay entry animations started.")
+                logger.d("Dream overlay entry animations started.")
             }
     }
 
@@ -242,11 +247,11 @@
                 doOnEnd {
                     mAnimator = null
                     mOverlayStateController.setExitAnimationsRunning(false)
-                    mLogger.d(TAG, "Dream overlay exit animations finished.")
+                    logger.d("Dream overlay exit animations finished.")
                 }
-                doOnCancel { mLogger.d(TAG, "Dream overlay exit animations canceled.") }
+                doOnCancel { logger.d("Dream overlay exit animations canceled.") }
                 start()
-                mLogger.d(TAG, "Dream overlay exit animations started.")
+                logger.d("Dream overlay exit animations started.")
             }
         mOverlayStateController.setExitAnimationsRunning(true)
         return mAnimator as AnimatorSet
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index c2421dc..c9748f9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -28,6 +28,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.DreamLog;
 import com.android.systemui.statusbar.policy.CallbackController;
 
 import java.util.ArrayList;
@@ -115,10 +117,10 @@
     public DreamOverlayStateController(@Main Executor executor,
             @Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled,
             FeatureFlags featureFlags,
-            DreamLogger dreamLogger) {
+            @DreamLog LogBuffer logBuffer) {
         mExecutor = executor;
         mOverlayEnabled = overlayEnabled;
-        mLogger = dreamLogger;
+        mLogger = new DreamLogger(logBuffer, TAG);
         mFeatureFlags = featureFlags;
         if (mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)) {
             mSupportedTypes = Complication.COMPLICATION_TYPE_NONE
@@ -126,7 +128,7 @@
         } else {
             mSupportedTypes = Complication.COMPLICATION_TYPE_NONE;
         }
-        mLogger.d(TAG, "Dream overlay enabled: " + mOverlayEnabled);
+        mLogger.logDreamOverlayEnabled(mOverlayEnabled);
     }
 
     /**
@@ -134,14 +136,13 @@
      */
     public void addComplication(Complication complication) {
         if (!mOverlayEnabled) {
-            mLogger.d(TAG,
-                    "Ignoring adding complication due to overlay disabled: " + complication);
+            mLogger.logIgnoreAddComplication("overlay disabled", complication.toString());
             return;
         }
 
         mExecutor.execute(() -> {
             if (mComplications.add(complication)) {
-                mLogger.d(TAG, "Added dream complication: " + complication);
+                mLogger.logAddComplication(complication.toString());
                 mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
             }
         });
@@ -152,14 +153,13 @@
      */
     public void removeComplication(Complication complication) {
         if (!mOverlayEnabled) {
-            mLogger.d(TAG,
-                    "Ignoring removing complication due to overlay disabled: " + complication);
+            mLogger.logIgnoreRemoveComplication("overlay disabled", complication.toString());
             return;
         }
 
         mExecutor.execute(() -> {
             if (mComplications.remove(complication)) {
-                mLogger.d(TAG, "Removed dream complication: " + complication);
+                mLogger.logRemoveComplication(complication.toString());
                 mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged());
             }
         });
@@ -305,7 +305,7 @@
      * @param active {@code true} if overlay is active, {@code false} otherwise.
      */
     public void setOverlayActive(boolean active) {
-        mLogger.d(TAG, "Dream overlay active: " + active);
+        mLogger.logOverlayActive(active);
         modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE);
     }
 
@@ -314,7 +314,7 @@
      * @param active {@code true} if low light mode is active, {@code false} otherwise.
      */
     public void setLowLightActive(boolean active) {
-        mLogger.d(TAG, "Low light mode active: " + active);
+        mLogger.logLowLightActive(active);
 
         if (isLowLightActive() && !active) {
             // Notify that we're exiting low light only on the transition from active to not active.
@@ -346,7 +346,7 @@
      * @param hasAttention {@code true} if has the user's attention, {@code false} otherwise.
      */
     public void setHasAssistantAttention(boolean hasAttention) {
-        mLogger.d(TAG, "Dream overlay has Assistant attention: " + hasAttention);
+        mLogger.logHasAssistantAttention(hasAttention);
         modifyState(hasAttention ? OP_SET_STATE : OP_CLEAR_STATE, STATE_HAS_ASSISTANT_ATTENTION);
     }
 
@@ -355,7 +355,7 @@
      * @param visible {@code true} if the status bar is visible, {@code false} otherwise.
      */
     public void setDreamOverlayStatusBarVisible(boolean visible) {
-        mLogger.d(TAG, "Dream overlay status bar visible: " + visible);
+        mLogger.logStatusBarVisible(visible);
         modifyState(
                 visible ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_STATUS_BAR_VISIBLE);
     }
@@ -373,7 +373,7 @@
      */
     public void setAvailableComplicationTypes(@Complication.ComplicationType int types) {
         mExecutor.execute(() -> {
-            mLogger.d(TAG, "Available complication types: " + types);
+            mLogger.logAvailableComplicationTypes(types);
             mAvailableComplicationTypes = types;
             mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
         });
@@ -391,7 +391,7 @@
      */
     public void setShouldShowComplications(boolean shouldShowComplications) {
         mExecutor.execute(() -> {
-            mLogger.d(TAG, "Should show complications: " + shouldShowComplications);
+            mLogger.logShouldShowComplications(shouldShowComplications);
             mShouldShowComplications = shouldShowComplications;
             mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged);
         });
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 3a28408..a6401b6 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -36,6 +36,8 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
 import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.dagger.DreamLog;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
@@ -161,7 +163,7 @@
             DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
             DreamOverlayStateController dreamOverlayStateController,
             UserTracker userTracker,
-            DreamLogger dreamLogger) {
+            @DreamLog LogBuffer logBuffer) {
         super(view);
         mResources = resources;
         mMainExecutor = mainExecutor;
@@ -177,7 +179,7 @@
         mZenModeController = zenModeController;
         mDreamOverlayStateController = dreamOverlayStateController;
         mUserTracker = userTracker;
-        mLogger = dreamLogger;
+        mLogger = new DreamLogger(logBuffer, TAG);
 
         // Register to receive show/hide updates for the system status bar. Our custom status bar
         // needs to hide when the system status bar is showing to ovoid overlapping status bars.
@@ -346,8 +348,8 @@
             @Nullable String contentDescription) {
         mMainExecutor.execute(() -> {
             if (mIsAttached) {
-                mLogger.d(TAG, (show ? "Showing" : "Hiding") + " dream status bar item: "
-                        + DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
+                mLogger.logShowOrHideStatusBarItem(
+                        show, DreamOverlayStatusBarView.getLoggableStatusIconType(iconType));
                 mView.showIcon(iconType, show, contentDescription);
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0670ec3..efa5981 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -254,7 +254,7 @@
     /** Migrate the indication area to the new keyguard root view. */
     // TODO(b/280067944): Tracking bug.
     @JvmField
-    val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+    val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area")
 
     /**
      * Migrate the bottom area to the new keyguard root view.
@@ -294,6 +294,11 @@
     @JvmField
     val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl")
 
+    /** Migrate the status view from the notification panel to keyguard root view. */
+    // TODO(b/291767565): Tracking bug.
+    @JvmField
+    val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
+
     // 300 - power menu
     // TODO(b/254512600): Tracking Bug
     @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -362,16 +367,6 @@
 
     // 600- status bar
 
-    // TODO(b/256614753): Tracking Bug
-    val NEW_STATUS_BAR_MOBILE_ICONS = releasedFlag(606, "new_status_bar_mobile_icons")
-
-    // TODO(b/256614751): Tracking Bug
-    val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND =
-        unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true)
-
-    // TODO(b/260881289): Tracking Bug
-    val NEW_STATUS_BAR_ICONS_DEBUG_COLORING =
-        unreleasedFlag(611, "new_status_bar_icons_debug_coloring")
 
     // TODO(b/265892345): Tracking Bug
     val PLUG_IN_STATUS_BAR_CHIP = releasedFlag(265892345, "plug_in_status_bar_chip")
@@ -411,12 +406,6 @@
     // TODO(b/254512502): Tracking Bug
     val MEDIA_SESSION_ACTIONS = unreleasedFlag(901, "media_session_actions")
 
-    // TODO(b/254512726): Tracking Bug
-    val MEDIA_NEARBY_DEVICES = releasedFlag(903, "media_nearby_devices")
-
-    // TODO(b/254512695): Tracking Bug
-    val MEDIA_MUTE_AWAIT = releasedFlag(904, "media_mute_await")
-
     // TODO(b/254512654): Tracking Bug
     @JvmField val DREAM_MEDIA_COMPLICATION = unreleasedFlag(905, "dream_media_complication")
 
@@ -432,9 +421,6 @@
     // TODO(b/263272731): Tracking Bug
     val MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE = releasedFlag(910, "media_ttt_receiver_success_ripple")
 
-    // TODO(b/265813373): Tracking Bug
-    val MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE = releasedFlag(912, "media_ttt_dismiss_gesture")
-
     // TODO(b/266157412): Tracking Bug
     val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions")
 
@@ -780,7 +766,8 @@
 
     // TODO(b/285174336): Tracking Bug
     @JvmField
-    val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing")
+    val USE_REPOS_FOR_BOUNCER_SHOWING =
+        unreleasedFlag(2900, "use_repos_for_bouncer_showing", teamfood = true)
 
     // 3100 - Haptic interactions
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
new file mode 100644
index 0000000..eaecda5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/flags/ViewRefactorFlag.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.flags
+
+import android.util.Log
+import com.android.systemui.Dependency
+
+/**
+ * This class promotes best practices for flag guarding System UI view refactors.
+ * * [isEnabled] allows changing an implementation.
+ * * [assertDisabled] allows authors to flag code as being "dead" when the flag gets enabled and
+ *   ensure that it is not being invoked accidentally in the post-flag refactor.
+ * * [expectEnabled] allows authors to guard new code with a "safe" alternative when invoked on
+ *   flag-disabled builds, but with a check that should crash eng builds or tests when the
+ *   expectation is violated.
+ *
+ * The constructors prefer that you provide a [FeatureFlags] instance, but does not require it,
+ * falling back to [Dependency.get]. This fallback should ONLY be used to flag-guard code changes
+ * inside views where injecting flag values after initialization can be error-prone.
+ */
+class ViewRefactorFlag
+private constructor(
+    private val injectedFlags: FeatureFlags?,
+    private val flag: BooleanFlag,
+    private val readFlagValue: (FeatureFlags) -> Boolean
+) {
+    @JvmOverloads
+    constructor(
+        flags: FeatureFlags? = null,
+        flag: UnreleasedFlag
+    ) : this(flags, flag, { it.isEnabled(flag) })
+
+    @JvmOverloads
+    constructor(
+        flags: FeatureFlags? = null,
+        flag: ReleasedFlag
+    ) : this(flags, flag, { it.isEnabled(flag) })
+
+    /** Whether the flag is enabled. Called to switch between an old behavior and a new behavior. */
+    val isEnabled by lazy {
+        @Suppress("DEPRECATION")
+        val featureFlags = injectedFlags ?: Dependency.get(FeatureFlags::class.java)
+        readFlagValue(featureFlags)
+    }
+
+    /**
+     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+     * the flag is enabled to ensure that the refactor author catches issues in testing.
+     *
+     * Example usage:
+     * ```
+     * public void setController(NotificationShelfController notificationShelfController) {
+     *     mShelfRefactor.assertDisabled();
+     *     mController = notificationShelfController;
+     * }
+     * ````
+     */
+    fun assertDisabled() = check(!isEnabled) { "Code path not supported when $flag is enabled." }
+
+    /**
+     * Called to ensure code is only run when the flag is enabled. This protects users from the
+     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+     * build to ensure that the refactor author catches issues in testing.
+     *
+     * Example usage:
+     * ```
+     * public void setShelfIcons(NotificationIconContainer icons) {
+     *     if (mShelfRefactor.expectEnabled()) {
+     *         mShelfIcons = icons;
+     *     }
+     * }
+     * ```
+     */
+    fun expectEnabled(): Boolean {
+        if (!isEnabled) {
+            val message = "Code path not supported when $flag is disabled."
+            Log.wtf(TAG, message, Exception(message))
+        }
+        return isEnabled
+    }
+
+    private companion object {
+        private const val TAG = "ViewRefactorFlag"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
deleted file mode 100644
index b304c3c..0000000
--- a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.hdmi;
-
-import android.hardware.hdmi.HdmiControlManager;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.tv.TvBottomSheetActivity;
-
-import javax.inject.Inject;
-
-/**
- * Confirmation dialog shown when Set Menu Language CEC message was received.
- */
-public class HdmiCecSetMenuLanguageActivity extends TvBottomSheetActivity
-        implements View.OnClickListener {
-    private static final String TAG = HdmiCecSetMenuLanguageActivity.class.getSimpleName();
-
-    private final HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
-
-    @Inject
-    public HdmiCecSetMenuLanguageActivity(
-            HdmiCecSetMenuLanguageHelper hdmiCecSetMenuLanguageHelper) {
-        mHdmiCecSetMenuLanguageHelper = hdmiCecSetMenuLanguageHelper;
-    }
-
-    @Override
-    public final void onCreate(Bundle b) {
-        super.onCreate(b);
-        getWindow().addPrivateFlags(
-                WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-        String languageTag = getIntent().getStringExtra(HdmiControlManager.EXTRA_LOCALE);
-        mHdmiCecSetMenuLanguageHelper.setLocale(languageTag);
-        if (mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()) {
-            finish();
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        CharSequence title = getString(R.string.hdmi_cec_set_menu_language_title,
-                mHdmiCecSetMenuLanguageHelper.getLocale().getDisplayLanguage());
-        CharSequence text = getString(R.string.hdmi_cec_set_menu_language_description);
-        initUI(title, text);
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v.getId() == R.id.bottom_sheet_positive_button) {
-            mHdmiCecSetMenuLanguageHelper.acceptLocale();
-        } else {
-            mHdmiCecSetMenuLanguageHelper.declineLocale();
-        }
-        finish();
-    }
-
-    void initUI(CharSequence title, CharSequence text) {
-        TextView titleTextView = findViewById(R.id.bottom_sheet_title);
-        TextView contentTextView = findViewById(R.id.bottom_sheet_body);
-        ImageView icon = findViewById(R.id.bottom_sheet_icon);
-        ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
-        Button okButton = findViewById(R.id.bottom_sheet_positive_button);
-        Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
-        titleTextView.setText(title);
-        contentTextView.setText(text);
-        icon.setImageResource(com.android.internal.R.drawable.ic_settings_language);
-        secondIcon.setVisibility(View.GONE);
-
-        okButton.setText(R.string.hdmi_cec_set_menu_language_accept);
-        okButton.setOnClickListener(this);
-
-        cancelButton.setText(R.string.hdmi_cec_set_menu_language_decline);
-        cancelButton.setOnClickListener(this);
-        cancelButton.requestFocus();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
deleted file mode 100644
index 1f61647..0000000
--- a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.hdmi;
-
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.app.LocalePicker;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.util.settings.SecureSettings;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.concurrent.Executor;
-
-import javax.inject.Inject;
-
-/**
- * Helper class to separate model and view for system language change initiated by HDMI CEC.
- */
-@SysUISingleton
-public class HdmiCecSetMenuLanguageHelper {
-    private static final String TAG = HdmiCecSetMenuLanguageHelper.class.getSimpleName();
-    private static final String SEPARATOR = ",";
-
-    private final Executor mBackgroundExecutor;
-    private final SecureSettings mSecureSettings;
-
-    private Locale mLocale;
-    private HashSet<String> mDenylist;
-
-    @Inject
-    public HdmiCecSetMenuLanguageHelper(@Background Executor executor,
-            SecureSettings secureSettings) {
-        mBackgroundExecutor = executor;
-        mSecureSettings = secureSettings;
-        String denylist = mSecureSettings.getStringForUser(
-                Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, UserHandle.USER_CURRENT);
-        mDenylist = new HashSet<>(denylist == null
-                ? Collections.EMPTY_SET
-                : Arrays.asList(denylist.split(SEPARATOR)));
-    }
-
-    /**
-     * Set internal locale based on given language tag.
-     */
-    public void setLocale(String languageTag) {
-        mLocale = Locale.forLanguageTag(languageTag);
-    }
-
-    /**
-     * Returns the locale from {@code <Set Menu Language>} CEC message.
-     */
-    public Locale getLocale() {
-        return mLocale;
-    }
-
-    /**
-     * Returns whether the locale from {@code <Set Menu Language>} CEC message was already
-     * denylisted.
-     */
-    public boolean isLocaleDenylisted() {
-        return mDenylist.contains(mLocale.toLanguageTag());
-    }
-
-    /**
-     * Accepts the new locale and updates system language.
-     */
-    public void acceptLocale() {
-        mBackgroundExecutor.execute(() -> LocalePicker.updateLocale(mLocale));
-    }
-
-    /**
-     * Declines the locale and puts it on the denylist.
-     */
-    public void declineLocale() {
-        mDenylist.add(mLocale.toLanguageTag());
-        mSecureSettings.putStringForUser(Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
-                String.join(SEPARATOR, mDenylist), UserHandle.USER_CURRENT);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 3646144..489d2ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -171,7 +171,6 @@
          * for the canned animation (if applicable) so interested parties can sync with it. If no
          * canned animation is playing, these are both 0.
          */
-        @JvmDefault
         fun onUnlockAnimationStarted(
             playingCannedAnimation: Boolean,
             isWakeAndUnlockNotFromDream: Boolean,
@@ -184,7 +183,6 @@
          * The keyguard is no longer visible in this state and the app/launcher behind the keyguard
          * is now completely visible.
          */
-        @JvmDefault
         fun onUnlockAnimationFinished() {}
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index 23f6fa6..03a270e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -25,19 +25,24 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
+import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
 import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
 import com.android.systemui.shade.NotificationShadeWindowView
 import com.android.systemui.statusbar.KeyguardIndicationController
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
 import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
 import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */
+@ExperimentalCoroutinesApi
 @SysUISingleton
 class KeyguardViewConfigurator
 @Inject
@@ -51,11 +56,14 @@
     private val indicationController: KeyguardIndicationController,
     private val keyguardLayoutManager: KeyguardLayoutManager,
     private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
+    private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
+    private val chipbarCoordinator: ChipbarCoordinator,
 ) : CoreStartable {
 
     private var indicationAreaHandle: DisposableHandle? = null
 
     override fun start() {
+        bindKeyguardRootView()
         val notificationPanel =
             notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
         bindIndicationArea(notificationPanel)
@@ -116,4 +124,13 @@
             }
         }
     }
+
+    private fun bindKeyguardRootView() {
+        KeyguardRootViewBinder.bind(
+            keyguardRootView,
+            featureFlags,
+            occludingAppDeviceEntryMessageViewModel,
+            chipbarCoordinator,
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9a32e94..e0834bb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -163,9 +163,11 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.settings.SystemSettings;
 import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
 import dagger.Lazy;
@@ -290,6 +292,8 @@
     public static final String SYS_BOOT_REASON_PROP = "sys.boot.reason.last";
     public static final String REBOOT_MAINLINE_UPDATE = "reboot,mainline_update";
     private final DreamOverlayStateController mDreamOverlayStateController;
+    private final JavaAdapter mJavaAdapter;
+    private final WallpaperRepository mWallpaperRepository;
 
     /** The stream type that the lock sounds are tied to. */
     private int mUiSoundsStreamType;
@@ -1323,6 +1327,8 @@
             KeyguardTransitions keyguardTransitions,
             InteractionJankMonitor interactionJankMonitor,
             DreamOverlayStateController dreamOverlayStateController,
+            JavaAdapter javaAdapter,
+            WallpaperRepository wallpaperRepository,
             Lazy<ShadeController> shadeControllerLazy,
             Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
             Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -1384,6 +1390,8 @@
         mScreenOffAnimationController = screenOffAnimationController;
         mInteractionJankMonitor = interactionJankMonitor;
         mDreamOverlayStateController = dreamOverlayStateController;
+        mJavaAdapter = javaAdapter;
+        mWallpaperRepository = wallpaperRepository;
 
         mActivityLaunchAnimator = activityLaunchAnimator;
         mScrimControllerLazy = scrimControllerLazy;
@@ -1487,6 +1495,10 @@
                 com.android.internal.R.anim.lock_screen_behind_enter);
 
         mWorkLockController = new WorkLockActivityController(mContext, mUserTracker);
+
+        mJavaAdapter.alwaysCollectFlow(
+                mWallpaperRepository.getWallpaperSupportsAmbientMode(),
+                this::setWallpaperSupportsAmbientMode);
     }
 
     // TODO(b/273443374) remove, temporary util to get a feature flag
@@ -2970,6 +2982,7 @@
     }
 
     private void onKeyguardExitFinished() {
+        if (DEBUG) Log.d(TAG, "onKeyguardExitFinished()");
         // only play "unlock" noises if not on a call (since the incall UI
         // disables the keyguard)
         if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
@@ -3191,13 +3204,14 @@
                 flags |= StatusBarManager.DISABLE_RECENT;
             }
 
-            if (mPowerGestureIntercepted) {
+            if (mPowerGestureIntercepted && mOccluded && isSecure()) {
                 flags |= StatusBarManager.DISABLE_RECENT;
             }
 
             if (DEBUG) {
                 Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
                         + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+                        + " mPowerGestureIntercepted=" + mPowerGestureIntercepted
                         +  " --> flags=0x" + Integer.toHexString(flags));
             }
 
@@ -3425,6 +3439,7 @@
         pw.print("  mPendingLock: "); pw.println(mPendingLock);
         pw.print("  wakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
         pw.print("  mPendingPinLock: "); pw.println(mPendingPinLock);
+        pw.print("  mPowerGestureIntercepted: "); pw.println(mPowerGestureIntercepted);
     }
 
     /**
@@ -3464,7 +3479,7 @@
      * In case it does support it, we have to fade in the incoming app, otherwise we'll reveal it
      * with the light reveal scrim.
      */
-    public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
+    private void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
         mWallpaperSupportsAmbientMode = supportsAmbientMode;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 1487408..a5ac7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -67,9 +67,11 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.settings.SystemSettings;
 import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
 import dagger.Lazy;
@@ -131,6 +133,8 @@
             KeyguardTransitions keyguardTransitions,
             InteractionJankMonitor interactionJankMonitor,
             DreamOverlayStateController dreamOverlayStateController,
+            JavaAdapter javaAdapter,
+            WallpaperRepository wallpaperRepository,
             Lazy<ShadeController> shadeController,
             Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -172,6 +176,8 @@
                 keyguardTransitions,
                 interactionJankMonitor,
                 dreamOverlayStateController,
+                javaAdapter,
+                wallpaperRepository,
                 shadeController,
                 notificationShadeWindowController,
                 activityLaunchAnimator,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index 0b6c7c4..ff3e77c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -325,6 +325,9 @@
 private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
     LockPatternUtils.StrongAuthTracker(context) {
 
+    private val selectedUserId =
+        userRepository.selectedUserInfo.map { it.id }.distinctUntilChanged()
+
     // Backing field for onStrongAuthRequiredChanged
     private val _authFlags =
         MutableStateFlow(AuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId)))
@@ -336,15 +339,12 @@
         )
 
     val currentUserAuthFlags: Flow<AuthenticationFlags> =
-        userRepository.selectedUserInfo
-            .map { it.id }
-            .distinctUntilChanged()
-            .flatMapLatest { userId ->
-                _authFlags
-                    .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
-                    .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
-                    .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
-            }
+        selectedUserId.flatMapLatest { userId ->
+            _authFlags
+                .map { AuthenticationFlags(userId, getStrongAuthForUser(userId)) }
+                .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
+                .onStart { emit(AuthenticationFlags(userId, getStrongAuthForUser(userId))) }
+        }
 
     /** isStrongBiometricAllowed for the current user. */
     val isStrongBiometricAllowed: Flow<Boolean> =
@@ -352,16 +352,17 @@
 
     /** isNonStrongBiometricAllowed for the current user. */
     val isNonStrongBiometricAllowed: Flow<Boolean> =
-        userRepository.selectedUserInfo
-            .map { it.id }
-            .distinctUntilChanged()
+        selectedUserId
             .flatMapLatest { userId ->
                 _nonStrongBiometricAllowed
                     .filter { it.first == userId }
                     .map { it.second }
-                    .onEach { Log.d(TAG, "isNonStrongBiometricAllowed changed for current user") }
+                    .onEach {
+                        Log.d(TAG, "isNonStrongBiometricAllowed changed for current user: $it")
+                    }
                     .onStart { emit(isNonStrongBiometricAllowedAfterIdleTimeout(userId)) }
             }
+            .and(isStrongBiometricAllowed)
 
     private val currentUserId
         get() = userRepository.getSelectedUserInfo().id
@@ -387,3 +388,6 @@
 
 private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
     (getKeyguardDisabledFeatures(null, userId) and policy) == 0
+
+private fun Flow<Boolean>.and(anotherFlow: Flow<Boolean>): Flow<Boolean> =
+    this.combine(anotherFlow) { a, b -> a && b }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index a3d1abe..d1f011e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -38,13 +38,13 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AcquiredFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.keyguard.shared.model.FailedFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.log.SessionTracker
@@ -88,10 +88,10 @@
     val canRunFaceAuth: StateFlow<Boolean>
 
     /** Provide the current status of face authentication. */
-    val authenticationStatus: Flow<AuthenticationStatus>
+    val authenticationStatus: Flow<FaceAuthenticationStatus>
 
     /** Provide the current status of face detection. */
-    val detectionStatus: Flow<DetectionStatus>
+    val detectionStatus: Flow<FaceDetectionStatus>
 
     /** Current state of whether face authentication is locked out or not. */
     val isLockedOut: StateFlow<Boolean>
@@ -151,13 +151,13 @@
     private var cancelNotReceivedHandlerJob: Job? = null
     private var halErrorRetryJob: Job? = null
 
-    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+    private val _authenticationStatus: MutableStateFlow<FaceAuthenticationStatus?> =
         MutableStateFlow(null)
-    override val authenticationStatus: Flow<AuthenticationStatus>
+    override val authenticationStatus: Flow<FaceAuthenticationStatus>
         get() = _authenticationStatus.filterNotNull()
 
-    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
-    override val detectionStatus: Flow<DetectionStatus>
+    private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
+    override val detectionStatus: Flow<FaceDetectionStatus>
         get() = _detectionStatus.filterNotNull()
 
     private val _isLockedOut = MutableStateFlow(false)
@@ -396,18 +396,18 @@
     private val faceAuthCallback =
         object : FaceManager.AuthenticationCallback() {
             override fun onAuthenticationFailed() {
-                _authenticationStatus.value = FailedAuthenticationStatus
+                _authenticationStatus.value = FailedFaceAuthenticationStatus
                 _isAuthenticated.value = false
                 faceAuthLogger.authenticationFailed()
                 onFaceAuthRequestCompleted()
             }
 
             override fun onAuthenticationAcquired(acquireInfo: Int) {
-                _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+                _authenticationStatus.value = AcquiredFaceAuthenticationStatus(acquireInfo)
             }
 
             override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
-                val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+                val errorStatus = ErrorFaceAuthenticationStatus(errorCode, errString.toString())
                 if (errorStatus.isLockoutError()) {
                     _isLockedOut.value = true
                 }
@@ -433,11 +433,11 @@
                 if (faceAcquiredInfoIgnoreList.contains(code)) {
                     return
                 }
-                _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+                _authenticationStatus.value = HelpFaceAuthenticationStatus(code, helpStr.toString())
             }
 
             override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
-                _authenticationStatus.value = SuccessAuthenticationStatus(result)
+                _authenticationStatus.value = SuccessFaceAuthenticationStatus(result)
                 _isAuthenticated.value = true
                 faceAuthLogger.faceAuthSuccess(result)
                 onFaceAuthRequestCompleted()
@@ -482,7 +482,7 @@
     private val detectionCallback =
         FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
             faceAuthLogger.faceDetected()
-            _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+            _detectionStatus.value = FaceDetectionStatus(sensorId, userId, isStrong)
         }
 
     private var cancellationInProgress = false
@@ -545,11 +545,11 @@
             faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
             return
         }
-        if (_isAuthRunning.value || detectCancellationSignal != null) {
+        if (_isAuthRunning.value) {
             faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
             return
         }
-
+        detectCancellationSignal?.cancel()
         detectCancellationSignal = CancellationSignal()
         withContext(mainDispatcher) {
             // We always want to invoke face detect in the main thread.
@@ -574,6 +574,7 @@
         if (authCancellationSignal == null) return
 
         authCancellationSignal?.cancel()
+        cancelNotReceivedHandlerJob?.cancel()
         cancelNotReceivedHandlerJob =
             applicationScope.launch {
                 delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
@@ -583,6 +584,7 @@
                     cancellationInProgress,
                     faceAuthRequestedWhileCancellation
                 )
+                _authenticationStatus.value = ErrorFaceAuthenticationStatus.cancelNotReceivedError()
                 onFaceAuthRequestCompleted()
             }
         cancellationInProgress = true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 52234b3..9bec300 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -21,27 +21,29 @@
 import android.hardware.biometrics.BiometricSourceType
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.systemui.Dumpable
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dump.DumpManager
-import java.io.PrintWriter
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 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.StateFlow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates state about device entry fingerprint auth mechanism. */
 interface DeviceEntryFingerprintAuthRepository {
     /** Whether the device entry fingerprint auth is locked out. */
-    val isLockedOut: StateFlow<Boolean>
+    val isLockedOut: Flow<Boolean>
 
     /**
      * Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
@@ -53,6 +55,9 @@
      * Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
      */
     val availableFpSensorType: Flow<BiometricType?>
+
+    /** Provide the current status of fingerprint authentication. */
+    val authenticationStatus: Flow<FingerprintAuthenticationStatus>
 }
 
 /**
@@ -69,16 +74,7 @@
     val authController: AuthController,
     val keyguardUpdateMonitor: KeyguardUpdateMonitor,
     @Application scope: CoroutineScope,
-    dumpManager: DumpManager,
-) : DeviceEntryFingerprintAuthRepository, Dumpable {
-
-    init {
-        dumpManager.registerDumpable(this)
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<String?>) {
-        pw.println("isLockedOut=${isLockedOut.value}")
-    }
+) : DeviceEntryFingerprintAuthRepository {
 
     override val availableFpSensorType: Flow<BiometricType?>
         get() {
@@ -114,7 +110,7 @@
         else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
     }
 
-    override val isLockedOut: StateFlow<Boolean> =
+    override val isLockedOut: Flow<Boolean> =
         conflatedCallbackFlow {
                 val sendLockoutUpdate =
                     fun() {
@@ -138,7 +134,7 @@
                 sendLockoutUpdate()
                 awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
             }
-            .stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
+            .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
 
     override val isRunning: Flow<Boolean>
         get() = conflatedCallbackFlow {
@@ -166,6 +162,93 @@
             awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
         }
 
+    override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+        get() = conflatedCallbackFlow {
+            val callback =
+                object : KeyguardUpdateMonitorCallback() {
+                    override fun onBiometricAuthenticated(
+                        userId: Int,
+                        biometricSourceType: BiometricSourceType,
+                        isStrongBiometric: Boolean,
+                    ) {
+
+                        sendUpdateIfFingerprint(
+                            biometricSourceType,
+                            SuccessFingerprintAuthenticationStatus(
+                                userId,
+                                isStrongBiometric,
+                            ),
+                        )
+                    }
+
+                    override fun onBiometricError(
+                        msgId: Int,
+                        errString: String?,
+                        biometricSourceType: BiometricSourceType,
+                    ) {
+                        sendUpdateIfFingerprint(
+                            biometricSourceType,
+                            ErrorFingerprintAuthenticationStatus(
+                                msgId,
+                                errString,
+                            ),
+                        )
+                    }
+
+                    override fun onBiometricHelp(
+                        msgId: Int,
+                        helpString: String?,
+                        biometricSourceType: BiometricSourceType,
+                    ) {
+                        sendUpdateIfFingerprint(
+                            biometricSourceType,
+                            HelpFingerprintAuthenticationStatus(
+                                msgId,
+                                helpString,
+                            ),
+                        )
+                    }
+
+                    override fun onBiometricAuthFailed(
+                        biometricSourceType: BiometricSourceType,
+                    ) {
+                        sendUpdateIfFingerprint(
+                            biometricSourceType,
+                            FailFingerprintAuthenticationStatus,
+                        )
+                    }
+
+                    override fun onBiometricAcquired(
+                        biometricSourceType: BiometricSourceType,
+                        acquireInfo: Int,
+                    ) {
+                        sendUpdateIfFingerprint(
+                            biometricSourceType,
+                            AcquiredFingerprintAuthenticationStatus(
+                                acquireInfo,
+                            ),
+                        )
+                    }
+
+                    private fun sendUpdateIfFingerprint(
+                        biometricSourceType: BiometricSourceType,
+                        authenticationStatus: FingerprintAuthenticationStatus
+                    ) {
+                        if (biometricSourceType != BiometricSourceType.FINGERPRINT) {
+                            return
+                        }
+
+                        trySendWithFailureLogging(
+                            authenticationStatus,
+                            TAG,
+                            "new fingerprint authentication status"
+                        )
+                    }
+                }
+            keyguardUpdateMonitor.registerCallback(callback)
+            awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+        }
+
     companion object {
         const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 42bee4a..7475c42 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -31,11 +31,13 @@
 import com.android.systemui.doze.DozeTransitionCallback
 import com.android.systemui.doze.DozeTransitionListener
 import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.keyguard.ScreenLifecycle
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -49,9 +51,11 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asSharedFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOn
@@ -148,6 +152,9 @@
     /** Observable for device wake/sleep state */
     val wakefulness: StateFlow<WakefulnessModel>
 
+    /** Observable for device screen state */
+    val screenModel: StateFlow<ScreenModel>
+
     /** Observable for biometric unlock modes */
     val biometricUnlockState: Flow<BiometricUnlockModel>
 
@@ -163,6 +170,9 @@
     /** Whether quick settings or quick-quick settings is visible. */
     val isQuickSettingsVisible: Flow<Boolean>
 
+    /** Receive an event for doze time tick */
+    val dozeTimeTick: Flow<Unit>
+
     /**
      * Returns `true` if the keyguard is showing; `false` otherwise.
      *
@@ -204,6 +214,8 @@
     fun setIsDozing(isDozing: Boolean)
 
     fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean)
+
+    fun dozeTimeTick()
 }
 
 /** Encapsulates application state for the keyguard. */
@@ -213,6 +225,7 @@
 constructor(
     statusBarStateController: StatusBarStateController,
     wakefulnessLifecycle: WakefulnessLifecycle,
+    screenLifecycle: ScreenLifecycle,
     biometricUnlockController: BiometricUnlockController,
     private val keyguardStateController: KeyguardStateController,
     private val keyguardBypassController: KeyguardBypassController,
@@ -370,6 +383,13 @@
         _isDozing.value = isDozing
     }
 
+    private val _dozeTimeTick = MutableSharedFlow<Unit>()
+    override val dozeTimeTick = _dozeTimeTick.asSharedFlow()
+
+    override fun dozeTimeTick() {
+        _dozeTimeTick.tryEmit(Unit)
+    }
+
     private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
     override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
 
@@ -559,6 +579,42 @@
                 initialValue = WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
             )
 
+    override val screenModel: StateFlow<ScreenModel> =
+        conflatedCallbackFlow {
+                val observer =
+                    object : ScreenLifecycle.Observer {
+                        override fun onScreenTurningOn() {
+                            dispatchNewState()
+                        }
+                        override fun onScreenTurnedOn() {
+                            dispatchNewState()
+                        }
+                        override fun onScreenTurningOff() {
+                            dispatchNewState()
+                        }
+                        override fun onScreenTurnedOff() {
+                            dispatchNewState()
+                        }
+
+                        private fun dispatchNewState() {
+                            trySendWithFailureLogging(
+                                ScreenModel.fromScreenLifecycle(screenLifecycle),
+                                TAG,
+                                "updated screen state",
+                            )
+                        }
+                    }
+
+                screenLifecycle.addObserver(observer)
+                awaitClose { screenLifecycle.removeObserver(observer) }
+            }
+            .stateIn(
+                scope,
+                // Use Eagerly so that we're always listening and never miss an event.
+                SharingStarted.Eagerly,
+                initialValue = ScreenModel.fromScreenLifecycle(screenLifecycle),
+            )
+
     override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
         fun sendFpLocation() {
             trySendWithFailureLogging(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
index abe59b7..27e3a74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -18,8 +18,8 @@
 
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -40,10 +40,10 @@
     private val _canRunFaceAuth = MutableStateFlow(false)
     override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth
 
-    override val authenticationStatus: Flow<AuthenticationStatus>
+    override val authenticationStatus: Flow<FaceAuthenticationStatus>
         get() = emptyFlow()
 
-    override val detectionStatus: Flow<DetectionStatus>
+    override val detectionStatus: Flow<FaceDetectionStatus>
         get() = emptyFlow()
 
     private val _isLockedOut = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt
new file mode 100644
index 0000000..c849b84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractor.kt
@@ -0,0 +1,138 @@
+/*
+ *  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.keyguard.domain.interactor
+
+import android.content.res.Resources
+import android.hardware.biometrics.BiometricSourceType
+import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED
+import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.map
+
+/**
+ * BiometricMessage business logic. Filters biometric error/acquired/fail/success events for
+ * authentication events that should never surface a message to the user at the current device
+ * state.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class BiometricMessageInteractor
+@Inject
+constructor(
+    @Main private val resources: Resources,
+    private val fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+    private val fingerprintPropertyRepository: FingerprintPropertyRepository,
+    private val indicationHelper: IndicationHelper,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+    val fingerprintErrorMessage: Flow<BiometricMessage> =
+        fingerprintAuthRepository.authenticationStatus
+            .filter {
+                it is ErrorFingerprintAuthenticationStatus &&
+                    !indicationHelper.shouldSuppressErrorMsg(FINGERPRINT, it.msgId)
+            }
+            .map {
+                val errorStatus = it as ErrorFingerprintAuthenticationStatus
+                BiometricMessage(
+                    FINGERPRINT,
+                    BiometricMessageType.ERROR,
+                    errorStatus.msgId,
+                    errorStatus.msg,
+                )
+            }
+
+    val fingerprintHelpMessage: Flow<BiometricMessage> =
+        fingerprintAuthRepository.authenticationStatus
+            .filter { it is HelpFingerprintAuthenticationStatus }
+            .filterNot { isPrimaryAuthRequired() }
+            .map {
+                val helpStatus = it as HelpFingerprintAuthenticationStatus
+                BiometricMessage(
+                    FINGERPRINT,
+                    BiometricMessageType.HELP,
+                    helpStatus.msgId,
+                    helpStatus.msg,
+                )
+            }
+
+    val fingerprintFailMessage: Flow<BiometricMessage> =
+        isUdfps().flatMapLatest { isUdfps ->
+            fingerprintAuthRepository.authenticationStatus
+                .filter { it is FailFingerprintAuthenticationStatus }
+                .filterNot { isPrimaryAuthRequired() }
+                .map {
+                    BiometricMessage(
+                        FINGERPRINT,
+                        BiometricMessageType.FAIL,
+                        BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+                        if (isUdfps) {
+                            resources.getString(
+                                com.android.internal.R.string.fingerprint_udfps_error_not_match
+                            )
+                        } else {
+                            resources.getString(
+                                com.android.internal.R.string.fingerprint_error_not_match
+                            )
+                        },
+                    )
+                }
+        }
+
+    private fun isUdfps() =
+        fingerprintPropertyRepository.sensorType.map {
+            it == FingerprintSensorType.UDFPS_OPTICAL ||
+                it == FingerprintSensorType.UDFPS_ULTRASONIC
+        }
+
+    private fun isPrimaryAuthRequired(): Boolean {
+        // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+        // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to
+        // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+        // check of whether non-strong biometric is allowed since strong biometrics can still be
+        // used.
+        return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
+    }
+}
+
+data class BiometricMessage(
+    val source: BiometricSourceType,
+    val type: BiometricMessageType,
+    val id: Int,
+    val message: String?,
+)
+
+enum class BiometricMessageType {
+    HELP,
+    ERROR,
+    FAIL,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
index 2efcd0c..0c898be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DozeInteractor.kt
@@ -35,4 +35,8 @@
     fun setLastTapToWakePosition(position: Point) {
         keyguardRepository.setLastDozeTapToWakePosition(position)
     }
+
+    fun dozeTimeTick() {
+        keyguardRepository.dozeTimeTick()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
index 74ef7a5..141b130 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractor.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
 import kotlinx.coroutines.flow.Flow
 
 /**
@@ -27,10 +27,10 @@
 interface KeyguardFaceAuthInteractor {
 
     /** Current authentication status */
-    val authenticationStatus: Flow<AuthenticationStatus>
+    val authenticationStatus: Flow<FaceAuthenticationStatus>
 
     /** Current detection status */
-    val detectionStatus: Flow<DetectionStatus>
+    val detectionStatus: Flow<FaceDetectionStatus>
 
     /** Can face auth be run right now */
     fun canFaceAuthRun(): Boolean
@@ -72,8 +72,8 @@
  */
 interface FaceAuthenticationListener {
     /** Receive face authentication status updates */
-    fun onAuthenticationStatusChanged(status: AuthenticationStatus)
+    fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
 
     /** Receive status updates whenever face detection runs */
-    fun onDetectionStatusChanged(status: DetectionStatus)
+    fun onDetectionStatusChanged(status: FaceDetectionStatus)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7fae752..1553525 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -70,6 +70,8 @@
     val dozeAmount: Flow<Float> = repository.linearDozeAmount
     /** Whether the system is in doze mode. */
     val isDozing: Flow<Boolean> = repository.isDozing
+    /** Receive an event for doze time tick */
+    val dozeTimeTick: Flow<Unit> = repository.dozeTimeTick
     /** Whether Always-on Display mode is available. */
     val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
     /** Doze transition information. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
index 5005b6c..10dd900 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/NoopKeyguardFaceAuthInteractor.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.emptyFlow
@@ -31,9 +31,9 @@
  */
 @SysUISingleton
 class NoopKeyguardFaceAuthInteractor @Inject constructor() : KeyguardFaceAuthInteractor {
-    override val authenticationStatus: Flow<AuthenticationStatus>
+    override val authenticationStatus: Flow<FaceAuthenticationStatus>
         get() = emptyFlow()
-    override val detectionStatus: Flow<DetectionStatus>
+    override val detectionStatus: Flow<FaceDetectionStatus>
         get() = emptyFlow()
 
     override fun canFaceAuthRun(): Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
new file mode 100644
index 0000000..a2287c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractor.kt
@@ -0,0 +1,138 @@
+/*
+ * 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.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.hardware.fingerprint.FingerprintManager
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.plugins.ActivityStarter
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.launch
+
+/** Business logic for handling authentication events when an app is occluding the lockscreen. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludingAppDeviceEntryInteractor
+@Inject
+constructor(
+    biometricMessageInteractor: BiometricMessageInteractor,
+    fingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+    keyguardInteractor: KeyguardInteractor,
+    primaryBouncerInteractor: PrimaryBouncerInteractor,
+    alternateBouncerInteractor: AlternateBouncerInteractor,
+    @Application scope: CoroutineScope,
+    private val context: Context,
+    activityStarter: ActivityStarter,
+) {
+    private val keyguardOccludedByApp: Flow<Boolean> =
+        combine(
+                keyguardInteractor.isKeyguardOccluded,
+                keyguardInteractor.isKeyguardShowing,
+                primaryBouncerInteractor.isShowing,
+                alternateBouncerInteractor.isVisible,
+            ) { occluded, showing, primaryBouncerShowing, alternateBouncerVisible ->
+                occluded && showing && !primaryBouncerShowing && !alternateBouncerVisible
+            }
+            .distinctUntilChanged()
+    private val fingerprintUnlockSuccessEvents: Flow<Unit> =
+        fingerprintAuthRepository.authenticationStatus
+            .ifKeyguardOccludedByApp()
+            .filter { it is SuccessFingerprintAuthenticationStatus }
+            .map {} // maps FingerprintAuthenticationStatus => Unit
+    private val fingerprintLockoutEvents: Flow<Unit> =
+        fingerprintAuthRepository.authenticationStatus
+            .ifKeyguardOccludedByApp()
+            .filter {
+                it is ErrorFingerprintAuthenticationStatus &&
+                    (it.msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT ||
+                        it.msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
+            }
+            .map {} // maps FingerprintAuthenticationStatus => Unit
+    val message: Flow<BiometricMessage?> =
+        merge(
+                biometricMessageInteractor.fingerprintErrorMessage,
+                biometricMessageInteractor.fingerprintFailMessage,
+                biometricMessageInteractor.fingerprintHelpMessage,
+            )
+            .ifKeyguardOccludedByApp(/* elseFlow */ flowOf(null))
+
+    init {
+        scope.launch {
+            // On fingerprint success, go to the home screen
+            fingerprintUnlockSuccessEvents.collect { goToHomeScreen() }
+        }
+
+        scope.launch {
+            // On device fingerprint lockout, request the bouncer with a runnable to
+            // go to the home screen. Without this, the bouncer won't proceed to the home screen.
+            fingerprintLockoutEvents.collect {
+                activityStarter.dismissKeyguardThenExecute(
+                    object : ActivityStarter.OnDismissAction {
+                        override fun onDismiss(): Boolean {
+                            goToHomeScreen()
+                            return false
+                        }
+
+                        override fun willRunAnimationOnKeyguard(): Boolean {
+                            return false
+                        }
+                    },
+                    /* cancel= */ null,
+                    /* afterKeyguardGone */ false
+                )
+            }
+        }
+    }
+
+    /** Launches an Activity which forces the current app to background by going home. */
+    private fun goToHomeScreen() {
+        context.startActivity(
+            Intent(Intent.ACTION_MAIN).apply {
+                addCategory(Intent.CATEGORY_HOME)
+                flags = Intent.FLAG_ACTIVITY_NEW_TASK
+            }
+        )
+    }
+
+    private fun <T> Flow<T>.ifKeyguardOccludedByApp(elseFlow: Flow<T> = emptyFlow()): Flow<T> {
+        return keyguardOccludedByApp.flatMapLatest { keyguardOccludedByApp ->
+            if (keyguardOccludedByApp) {
+                this
+            } else {
+                elseFlow
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
index d467225..6e7a20b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.log.FaceAuthenticationLogger
 import com.android.systemui.util.kotlin.pairwise
@@ -165,10 +165,10 @@
         repository.cancel()
     }
 
-    private val _authenticationStatusOverride = MutableStateFlow<AuthenticationStatus?>(null)
+    private val faceAuthenticationStatusOverride = MutableStateFlow<FaceAuthenticationStatus?>(null)
     /** Provide the status of face authentication */
     override val authenticationStatus =
-        merge(_authenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
+        merge(faceAuthenticationStatusOverride.filterNotNull(), repository.authenticationStatus)
 
     /** Provide the status of face detection */
     override val detectionStatus = repository.detectionStatus
@@ -176,13 +176,13 @@
     private fun runFaceAuth(uiEvent: FaceAuthUiEvent, fallbackToDetect: Boolean) {
         if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
             if (repository.isLockedOut.value) {
-                _authenticationStatusOverride.value =
-                    ErrorAuthenticationStatus(
+                faceAuthenticationStatusOverride.value =
+                    ErrorFaceAuthenticationStatus(
                         BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT,
                         context.resources.getString(R.string.keyguard_face_unlock_unavailable)
                     )
             } else {
-                _authenticationStatusOverride.value = null
+                faceAuthenticationStatusOverride.value = null
                 applicationScope.launch {
                     faceAuthenticationLogger.authRequested(uiEvent)
                     repository.authenticate(uiEvent, fallbackToDetection = fallbackToDetect)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
index bba0e37..c0308e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
@@ -21,10 +21,16 @@
 import android.animation.IntEvaluator
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.statusbar.phone.hideAffordancesRequest
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onStart
 
 /** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
 @ExperimentalCoroutinesApi
@@ -35,6 +41,8 @@
     configRepo: ConfigurationRepository,
     burnInInteractor: BurnInInteractor,
     keyguardInteractor: KeyguardInteractor,
+    shadeRepository: ShadeRepository,
+    dialogManager: SystemUIDialogManager,
 ) {
     private val intEvaluator = IntEvaluator()
     private val floatEvaluator = FloatEvaluator()
@@ -56,6 +64,26 @@
                 floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
             )
         }
+
+    val dialogHideAffordancesRequest: Flow<Boolean> = dialogManager.hideAffordancesRequest
+
+    val qsProgress: Flow<Float> =
+        shadeRepository.qsExpansion // swipe from top of LS
+            .map { (it * 2).coerceIn(0f, 1f) }
+            .onStart { emit(0f) }
+
+    val shadeExpansion: Flow<Float> =
+        combine(
+                shadeRepository.udfpsTransitionToFullShadeProgress, // swipe from middle of LS
+                keyguardInteractor.statusBarState, // quick swipe from middle of LS
+            ) { shadeProgress, statusBarState ->
+                if (statusBarState == StatusBarState.SHADE_LOCKED) {
+                    1f
+                } else {
+                    shadeProgress
+                }
+            }
+            .onStart { emit(0f) }
 }
 
 data class BurnInOffsets(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index b354cfd..d9792cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -23,33 +23,36 @@
  * Authentication status provided by
  * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
  */
-sealed class AuthenticationStatus
-
-/** Success authentication status. */
-data class SuccessAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
-    AuthenticationStatus()
-
-/** Face authentication help message. */
-data class HelpAuthenticationStatus(val msgId: Int, val msg: String?) : AuthenticationStatus()
-
-/** Face acquired message. */
-data class AcquiredAuthenticationStatus(val acquiredInfo: Int) : AuthenticationStatus()
-
-/** Face authentication failed message. */
-object FailedAuthenticationStatus : AuthenticationStatus()
-
-/** Face authentication error message */
-data class ErrorAuthenticationStatus(
-    val msgId: Int,
-    val msg: String? = null,
+sealed class FaceAuthenticationStatus(
     // present to break equality check if the same error occurs repeatedly.
     val createdAt: Long = elapsedRealtime()
-) : AuthenticationStatus() {
+)
+
+/** Success authentication status. */
+data class SuccessFaceAuthenticationStatus(val successResult: FaceManager.AuthenticationResult) :
+    FaceAuthenticationStatus()
+
+/** Face authentication help message. */
+data class HelpFaceAuthenticationStatus(val msgId: Int, val msg: String?) :
+    FaceAuthenticationStatus()
+
+/** Face acquired message. */
+data class AcquiredFaceAuthenticationStatus(val acquiredInfo: Int) : FaceAuthenticationStatus()
+
+/** Face authentication failed message. */
+object FailedFaceAuthenticationStatus : FaceAuthenticationStatus()
+
+/** Face authentication error message */
+data class ErrorFaceAuthenticationStatus(
+    val msgId: Int,
+    val msg: String? = null,
+) : FaceAuthenticationStatus() {
     /**
      * Method that checks if [msgId] is a lockout error. A lockout error means that face
      * authentication is locked out.
      */
-    fun isLockoutError() = msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
+    fun isLockoutError() =
+        msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT || msgId == FaceManager.FACE_ERROR_LOCKOUT
 
     /**
      * Method that checks if [msgId] is a cancellation error. This means that face authentication
@@ -61,7 +64,21 @@
     fun isHardwareError() =
         msgId == FaceManager.FACE_ERROR_HW_UNAVAILABLE ||
             msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS
+
+    companion object {
+        /**
+         * Error message that is created when cancel confirmation is not received from FaceManager
+         * after we request for a cancellation of face auth.
+         */
+        fun cancelNotReceivedError() = ErrorFaceAuthenticationStatus(-1, "")
+    }
 }
 
 /** Face detection success message. */
-data class DetectionStatus(val sensorId: Int, val userId: Int, val isStrongBiometric: Boolean)
+data class FaceDetectionStatus(
+    val sensorId: Int,
+    val userId: Int,
+    val isStrongBiometric: Boolean,
+    // present to break equality check if the same error occurs repeatedly.
+    val createdAt: Long = elapsedRealtime()
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
new file mode 100644
index 0000000..7fc6016
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
@@ -0,0 +1,52 @@
+/*
+ * 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.keyguard.shared.model
+
+import android.os.SystemClock.elapsedRealtime
+
+/**
+ * Fingerprint authentication status provided by
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository]
+ */
+sealed class FingerprintAuthenticationStatus
+
+/** Fingerprint authentication success status. */
+data class SuccessFingerprintAuthenticationStatus(
+    val userId: Int,
+    val isStrongBiometric: Boolean,
+) : FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication help message. */
+data class HelpFingerprintAuthenticationStatus(
+    val msgId: Int,
+    val msg: String?,
+) : FingerprintAuthenticationStatus()
+
+/** Fingerprint acquired message. */
+data class AcquiredFingerprintAuthenticationStatus(val acquiredInfo: Int) :
+    FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication failed message. */
+object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
+
+/** Fingerprint authentication error message */
+data class ErrorFingerprintAuthenticationStatus(
+    val msgId: Int,
+    val msg: String? = null,
+    // present to break equality check if the same error occurs repeatedly.
+    val createdAt: Long = elapsedRealtime(),
+) : FingerprintAuthenticationStatus()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt
new file mode 100644
index 0000000..80a1b75
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenModel.kt
@@ -0,0 +1,29 @@
+/*
+ * 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.keyguard.shared.model
+
+import com.android.systemui.keyguard.ScreenLifecycle
+
+/** Model device screen lifecycle states. */
+data class ScreenModel(
+    val state: ScreenState,
+) {
+    companion object {
+        fun fromScreenLifecycle(screenLifecycle: ScreenLifecycle): ScreenModel {
+            return ScreenModel(ScreenState.fromScreenLifecycleInt(screenLifecycle.getScreenState()))
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt
new file mode 100644
index 0000000..fe5d935
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/ScreenState.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.ScreenLifecycle
+
+enum class ScreenState {
+    /** Screen is fully off. */
+    SCREEN_OFF,
+    /** Signal that the screen is turning on. */
+    SCREEN_TURNING_ON,
+    /** Screen is fully on. */
+    SCREEN_ON,
+    /** Signal that the screen is turning off. */
+    SCREEN_TURNING_OFF;
+
+    companion object {
+        fun fromScreenLifecycleInt(value: Int): ScreenState {
+            return when (value) {
+                ScreenLifecycle.SCREEN_OFF -> SCREEN_OFF
+                ScreenLifecycle.SCREEN_TURNING_ON -> SCREEN_TURNING_ON
+                ScreenLifecycle.SCREEN_ON -> SCREEN_ON
+                ScreenLifecycle.SCREEN_TURNING_OFF -> SCREEN_TURNING_OFF
+                else -> throw IllegalArgumentException("Invalid screen value: $value")
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
new file mode 100644
index 0000000..1db596b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -0,0 +1,93 @@
+/*
+ * 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.keyguard.ui.binder
+
+import android.annotation.DrawableRes
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Text
+import com.android.systemui.common.shared.model.TintedIcon
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.temporarydisplay.ViewPriority
+import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
+import com.android.systemui.temporarydisplay.chipbar.ChipbarInfo
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+/** Bind occludingAppDeviceEntryMessageViewModel to run whenever the keyguard view is attached. */
+@ExperimentalCoroutinesApi
+object KeyguardRootViewBinder {
+    @JvmStatic
+    fun bind(
+        view: ViewGroup,
+        featureFlags: FeatureFlags,
+        occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
+        chipbarCoordinator: ChipbarCoordinator,
+    ) {
+        if (featureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
+            view.repeatWhenAttached {
+                repeatOnLifecycle(Lifecycle.State.CREATED) {
+                    launch {
+                        occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage
+                            ->
+                            if (biometricMessage?.message != null) {
+                                chipbarCoordinator.displayView(
+                                    createChipbarInfo(
+                                        biometricMessage.message,
+                                        R.drawable.ic_lock,
+                                    )
+                                )
+                            } else {
+                                chipbarCoordinator.removeView(ID, "occludingAppMsgNull")
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates an instance of [ChipbarInfo] that can be sent to [ChipbarCoordinator] for display.
+     */
+    private fun createChipbarInfo(message: String, @DrawableRes icon: Int): ChipbarInfo {
+        return ChipbarInfo(
+            startIcon =
+                TintedIcon(
+                    Icon.Resource(icon, null),
+                    ChipbarInfo.DEFAULT_ICON_TINT,
+                ),
+            text = Text.Loaded(message),
+            endItem = null,
+            vibrationEffect = null,
+            windowTitle = "OccludingAppUnlockMsgChip",
+            wakeReason = "OCCLUDING_APP_UNLOCK_MSG_CHIP",
+            timeoutMs = 3500,
+            id = ID,
+            priority = ViewPriority.CRITICAL,
+            instanceId = null,
+        )
+    }
+
+    private const val ID = "occluding_app_device_entry_unlock_msg"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
index 728dd39..9872d97 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -37,6 +37,7 @@
         view: LottieAnimationView,
         viewModel: UdfpsAodViewModel,
     ) {
+        view.alpha = 0f
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
index 26ef468..0113628 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
@@ -38,6 +38,7 @@
         view: ImageView,
         viewModel: BackgroundViewModel,
     ) {
+        view.alpha = 0f
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
index 0ab8e52..bab04f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -42,6 +42,7 @@
         view: LottieAnimationView,
         viewModel: FingerprintViewModel,
     ) {
+        view.alpha = 0f
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
new file mode 100644
index 0000000..3a162d7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludingAppDeviceEntryMessageViewModel.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.BiometricMessage
+import com.android.systemui.keyguard.domain.interactor.OccludingAppDeviceEntryInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/** Shows authentication messages over occcluding apps over the lockscreen. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludingAppDeviceEntryMessageViewModel
+@Inject
+constructor(
+    interactor: OccludingAppDeviceEntryInteractor,
+) {
+    val message: Flow<BiometricMessage?> = interactor.message
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
index fd4b666..b307f1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -21,13 +21,18 @@
 import com.android.settingslib.Utils.getColorAttrDefaultColor
 import com.android.systemui.R
 import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.wm.shell.animation.Interpolators
 import javax.inject.Inject
 import kotlin.math.roundToInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 
@@ -38,6 +43,8 @@
     lockscreenColorResId: Int,
     alternateBouncerColorResId: Int,
     transitionInteractor: KeyguardTransitionInteractor,
+    udfpsKeyguardInteractor: UdfpsKeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
 ) {
     private val toLockscreen: Flow<TransitionViewModel> =
         transitionInteractor.anyStateToLockscreenTransition.map {
@@ -54,46 +61,53 @@
         }
 
     private val toAlternateBouncer: Flow<TransitionViewModel> =
-        transitionInteractor.anyStateToAlternateBouncerTransition.map {
-            TransitionViewModel(
-                alpha = 1f,
-                scale =
-                    if (visibleInKeyguardState(it.from)) {
-                        1f
-                    } else {
-                        it.value
-                    },
-                color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
-            )
+        keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
+            transitionInteractor.anyStateToAlternateBouncerTransition.map {
+                TransitionViewModel(
+                    alpha = 1f,
+                    scale =
+                        if (visibleInKeyguardState(it.from, statusBarState)) {
+                            1f
+                        } else {
+                            Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
+                        },
+                    color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
+                )
+            }
         }
 
     private val fadeOut: Flow<TransitionViewModel> =
-        merge(
-                transitionInteractor.anyStateToGoneTransition,
-                transitionInteractor.anyStateToAodTransition,
-                transitionInteractor.anyStateToOccludedTransition,
-                transitionInteractor.anyStateToPrimaryBouncerTransition,
-                transitionInteractor.anyStateToDreamingTransition,
-            )
-            .map {
-                TransitionViewModel(
-                    alpha =
-                        if (visibleInKeyguardState(it.from)) {
-                            1f - it.value
-                        } else {
-                            0f
-                        },
-                    scale = 1f,
-                    color =
-                        if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
-                            getColorAttrDefaultColor(context, alternateBouncerColorResId)
-                        } else {
-                            getColorAttrDefaultColor(context, lockscreenColorResId)
-                        },
+        keyguardInteractor.statusBarState.flatMapLatest { statusBarState ->
+            merge(
+                    transitionInteractor.anyStateToGoneTransition,
+                    transitionInteractor.anyStateToAodTransition,
+                    transitionInteractor.anyStateToOccludedTransition,
+                    transitionInteractor.anyStateToPrimaryBouncerTransition,
+                    transitionInteractor.anyStateToDreamingTransition,
                 )
-            }
+                .map {
+                    TransitionViewModel(
+                        alpha =
+                            if (visibleInKeyguardState(it.from, statusBarState)) {
+                                1f - it.value
+                            } else {
+                                0f
+                            },
+                        scale = 1f,
+                        color =
+                            if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
+                                getColorAttrDefaultColor(context, alternateBouncerColorResId)
+                            } else {
+                                getColorAttrDefaultColor(context, lockscreenColorResId)
+                            },
+                    )
+                }
+        }
 
-    private fun visibleInKeyguardState(state: KeyguardState): Boolean {
+    private fun visibleInKeyguardState(
+        state: KeyguardState,
+        statusBarState: StatusBarState
+    ): Boolean {
         return when (state) {
             KeyguardState.OFF,
             KeyguardState.DOZING,
@@ -102,17 +116,53 @@
             KeyguardState.PRIMARY_BOUNCER,
             KeyguardState.GONE,
             KeyguardState.OCCLUDED -> false
-            KeyguardState.LOCKSCREEN,
+            KeyguardState.LOCKSCREEN -> statusBarState == StatusBarState.KEYGUARD
             KeyguardState.ALTERNATE_BOUNCER -> true
         }
     }
 
-    val transition: Flow<TransitionViewModel> =
+    private val keyguardStateTransition =
         merge(
             toAlternateBouncer,
             toLockscreen,
             fadeOut,
         )
+
+    private val dialogHideAffordancesAlphaMultiplier: Flow<Float> =
+        udfpsKeyguardInteractor.dialogHideAffordancesRequest.map { hideAffordances ->
+            if (hideAffordances) {
+                0f
+            } else {
+                1f
+            }
+        }
+
+    private val alphaMultiplier: Flow<Float> =
+        combine(
+            transitionInteractor.startedKeyguardState,
+            dialogHideAffordancesAlphaMultiplier,
+            udfpsKeyguardInteractor.shadeExpansion,
+            udfpsKeyguardInteractor.qsProgress,
+        ) { startedKeyguardState, dialogHideAffordancesAlphaMultiplier, shadeExpansion, qsProgress
+            ->
+            if (startedKeyguardState == KeyguardState.ALTERNATE_BOUNCER) {
+                1f
+            } else {
+                dialogHideAffordancesAlphaMultiplier * (1f - shadeExpansion) * (1f - qsProgress)
+            }
+        }
+
+    val transition: Flow<TransitionViewModel> =
+        combine(
+            alphaMultiplier,
+            keyguardStateTransition,
+        ) { alphaMultiplier, keyguardStateTransition ->
+            TransitionViewModel(
+                alpha = keyguardStateTransition.alpha * alphaMultiplier,
+                scale = keyguardStateTransition.scale,
+                color = keyguardStateTransition.color,
+            )
+        }
     val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
 }
 
@@ -123,12 +173,15 @@
     val context: Context,
     transitionInteractor: KeyguardTransitionInteractor,
     interactor: UdfpsKeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
 ) :
     UdfpsLockscreenViewModel(
         context,
         android.R.attr.textColorPrimary,
         com.android.internal.R.attr.materialColorOnPrimaryFixed,
         transitionInteractor,
+        interactor,
+        keyguardInteractor,
     ) {
     val dozeAmount: Flow<Float> = interactor.dozeAmount
     val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
@@ -147,12 +200,16 @@
 constructor(
     val context: Context,
     transitionInteractor: KeyguardTransitionInteractor,
+    interactor: UdfpsKeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
 ) :
     UdfpsLockscreenViewModel(
         context,
         com.android.internal.R.attr.colorSurface,
         com.android.internal.R.attr.materialColorPrimaryFixed,
         transitionInteractor,
+        interactor,
+        keyguardInteractor,
     )
 
 data class TransitionViewModel(
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 68cdfb6..373f705 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -4,7 +4,7 @@
 import android.hardware.face.FaceSensorPropertiesInternal
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.log.core.LogLevel.DEBUG
 import com.android.systemui.log.dagger.FaceAuthLog
@@ -240,7 +240,7 @@
         )
     }
 
-    fun hardwareError(errorStatus: ErrorAuthenticationStatus) {
+    fun hardwareError(errorStatus: ErrorFaceAuthenticationStatus) {
         logBuffer.log(
             TAG,
             DEBUG,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 8d3c6d5..8f884d24 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -30,6 +30,9 @@
 import android.os.ResultReceiver
 import android.os.UserHandle
 import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
 import com.android.internal.annotations.VisibleForTesting
 import com.android.internal.app.AbstractMultiProfilePagerAdapter.EmptyStateProvider
 import com.android.internal.app.AbstractMultiProfilePagerAdapter.MyUserIdProvider
@@ -54,7 +57,11 @@
     /** This is used to override the dependency in a screenshot test */
     @VisibleForTesting
     private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?
-) : ChooserActivity(), MediaProjectionAppSelectorView, MediaProjectionAppSelectorResultHandler {
+) :
+    ChooserActivity(),
+    MediaProjectionAppSelectorView,
+    MediaProjectionAppSelectorResultHandler,
+    LifecycleOwner {
 
     @Inject
     constructor(
@@ -62,6 +69,8 @@
         activityLauncher: AsyncActivityLauncher
     ) : this(componentFactory, activityLauncher, listControllerFactory = null)
 
+    private val lifecycleRegistry = LifecycleRegistry(this)
+    override val lifecycle = lifecycleRegistry
     private lateinit var configurationController: ConfigurationController
     private lateinit var controller: MediaProjectionAppSelectorController
     private lateinit var recentsViewController: MediaProjectionRecentsViewController
@@ -75,7 +84,9 @@
     override fun getLayoutResource() = R.layout.media_projection_app_selector
 
     public override fun onCreate(bundle: Bundle?) {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
         component = componentFactory.create(activity = this, view = this, resultHandler = this)
+        component.lifecycleObservers.forEach { lifecycle.addObserver(it) }
 
         // Create a separate configuration controller for this activity as the configuration
         // might be different from the global one
@@ -96,6 +107,26 @@
         controller.init()
     }
 
+    override fun onStart() {
+        super.onStart()
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+    }
+
+    override fun onResume() {
+        super.onResume()
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    }
+
+    override fun onPause() {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+        super.onPause()
+    }
+
+    override fun onStop() {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP)
+        super.onStop()
+    }
+
     override fun onConfigurationChanged(newConfig: Configuration) {
         super.onConfigurationChanged(newConfig)
         configurationController.onConfigurationChanged(newConfig)
@@ -152,6 +183,8 @@
     }
 
     override fun onDestroy() {
+        lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+        component.lifecycleObservers.forEach { lifecycle.removeObserver(it) }
         // onDestroy is also called when an app is selected, in that case we only want to send
         // RECORD_CONTENT_TASK but not RECORD_CANCEL
         if (!taskSelected) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
index 6ad36ca..c90a3c1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt
@@ -144,7 +144,7 @@
         val oldKey: String?,
         val controller: MediaController?,
         val localMediaManager: LocalMediaManager,
-        val muteAwaitConnectionManager: MediaMuteAwaitConnectionManager?
+        val muteAwaitConnectionManager: MediaMuteAwaitConnectionManager,
     ) :
         LocalMediaManager.DeviceCallback,
         MediaController.Callback(),
@@ -180,7 +180,7 @@
                 if (!started) {
                     localMediaManager.registerCallback(this)
                     localMediaManager.startScan()
-                    muteAwaitConnectionManager?.startListening()
+                    muteAwaitConnectionManager.startListening()
                     playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN
                     playbackVolumeControlId = controller?.playbackInfo?.volumeControlId
                     controller?.registerCallback(this)
@@ -198,7 +198,7 @@
                     controller?.unregisterCallback(this)
                     localMediaManager.stopScan()
                     localMediaManager.unregisterCallback(this)
-                    muteAwaitConnectionManager?.stopListening()
+                    muteAwaitConnectionManager.stopListening()
                     configurationController.removeCallback(configListener)
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 09aef88..f2db088 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -34,15 +34,6 @@
         return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS)
     }
 
-    /** Check whether we support displaying information about mute await connections. */
-    fun areMuteAwaitConnectionsEnabled() = featureFlags.isEnabled(Flags.MEDIA_MUTE_AWAIT)
-
-    /**
-     * Check whether we enable support for nearby media devices. See
-     * [android.app.StatusBarManager.registerNearbyMediaDevicesProvider] for more information.
-     */
-    fun areNearbyMediaDevicesEnabled() = featureFlags.isEnabled(Flags.MEDIA_NEARBY_DEVICES)
-
     /**
      * If true, keep active media controls for the lifetime of the MediaSession, regardless of
      * whether the underlying notification was dismissed
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 46efac5..888cd0b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -23,10 +23,7 @@
 import com.android.systemui.media.controls.ui.MediaHierarchyManager;
 import com.android.systemui.media.controls.ui.MediaHost;
 import com.android.systemui.media.controls.ui.MediaHostStatesManager;
-import com.android.systemui.media.controls.util.MediaFlags;
 import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
-import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
-import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
 import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
 import com.android.systemui.media.taptotransfer.MediaTttFlags;
 import com.android.systemui.media.taptotransfer.receiver.MediaTttReceiverLogBuffer;
@@ -119,29 +116,4 @@
         }
         return Optional.of(helperLazy.get());
     }
-
-    /** */
-    @Provides
-    @SysUISingleton
-    static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
-            MediaFlags mediaFlags,
-            Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
-    ) {
-        if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
-            return Optional.empty();
-        }
-        return Optional.of(muteAwaitConnectionCliLazy.get());
-    }
-
-    /** */
-    @Provides
-    @SysUISingleton
-    static Optional<NearbyMediaDevicesManager> providesNearbyMediaDevicesManager(
-            MediaFlags mediaFlags,
-            Lazy<NearbyMediaDevicesManager> nearbyMediaDevicesManagerLazy) {
-        if (!mediaFlags.areNearbyMediaDevicesEnabled()) {
-            return Optional.empty();
-        }
-        return Optional.of(nearbyMediaDevicesManagerLazy.get());
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
index a1e9995..18d5103 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import java.util.Optional
 import javax.inject.Inject
 
 /**
@@ -46,7 +45,7 @@
     private val notifCollection: CommonNotifCollection,
     private val uiEventLogger: UiEventLogger,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
-    private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>,
+    private val nearbyMediaDevicesManager: NearbyMediaDevicesManager,
     private val audioManager: AudioManager,
     private val powerExemptionManager: PowerExemptionManager,
     private val keyGuardManager: KeyguardManager,
@@ -62,7 +61,7 @@
 
         val controller = MediaOutputController(context, packageName,
                 mediaSessionManager, lbm, starter, notifCollection,
-                dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager,
+                dialogLaunchAnimator, nearbyMediaDevicesManager, audioManager,
                 powerExemptionManager, keyGuardManager, featureFlags, userTracker)
         val dialog =
                 MediaOutputBroadcastDialog(context, aboveStatusBar, broadcastSender, controller)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index f87f53c..83631b0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -100,7 +100,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -178,7 +177,7 @@
             lbm, ActivityStarter starter,
             CommonNotifCollection notifCollection,
             DialogLaunchAnimator dialogLaunchAnimator,
-            Optional<NearbyMediaDevicesManager> nearbyMediaDevicesManagerOptional,
+            NearbyMediaDevicesManager nearbyMediaDevicesManager,
             AudioManager audioManager,
             PowerExemptionManager powerExemptionManager,
             KeyguardManager keyGuardManager,
@@ -199,7 +198,7 @@
         mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
         mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
         mDialogLaunchAnimator = dialogLaunchAnimator;
-        mNearbyMediaDevicesManager = nearbyMediaDevicesManagerOptional.orElse(null);
+        mNearbyMediaDevicesManager = nearbyMediaDevicesManager;
         mColorItemContent = Utils.getColorStateListDefaultColor(mContext,
                 R.color.media_dialog_item_main_content);
         mColorSeekbarProgress = Utils.getColorStateListDefaultColor(mContext,
@@ -826,13 +825,7 @@
     }
 
     List<RoutingSessionInfo> getActiveRemoteMediaDevices() {
-        final List<RoutingSessionInfo> sessionInfos = new ArrayList<>();
-        for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) {
-            if (!info.isSystemSession()) {
-                sessionInfos.add(info);
-            }
-        }
-        return sessionInfos;
+        return new ArrayList<>(mLocalMediaManager.getRemoteRoutingSessions());
     }
 
     void adjustVolume(MediaDevice device, int volume) {
@@ -928,7 +921,7 @@
     void launchMediaOutputBroadcastDialog(View mediaOutputDialog, BroadcastSender broadcastSender) {
         MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
                 mMediaSessionManager, mLocalBluetoothManager, mActivityStarter,
-                mNotifCollection, mDialogLaunchAnimator, Optional.of(mNearbyMediaDevicesManager),
+                mNotifCollection, mDialogLaunchAnimator, mNearbyMediaDevicesManager,
                 mAudioManager, mPowerExemptionManager, mKeyGuardManager, mFeatureFlags,
                 mUserTracker);
         MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
index 4c168ec..af65937 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
-import java.util.Optional
 import javax.inject.Inject
 
 /**
@@ -48,7 +47,7 @@
     private val notifCollection: CommonNotifCollection,
     private val uiEventLogger: UiEventLogger,
     private val dialogLaunchAnimator: DialogLaunchAnimator,
-    private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>,
+    private val nearbyMediaDevicesManager: NearbyMediaDevicesManager,
     private val audioManager: AudioManager,
     private val powerExemptionManager: PowerExemptionManager,
     private val keyGuardManager: KeyguardManager,
@@ -68,7 +67,7 @@
         val controller = MediaOutputController(
             context, packageName,
             mediaSessionManager, lbm, starter, notifCollection,
-            dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager,
+            dialogLaunchAnimator, nearbyMediaDevicesManager, audioManager,
             powerExemptionManager, keyGuardManager, featureFlags, userTracker)
         val dialog =
             MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller,
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
index e260894..97ec654 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
@@ -21,14 +21,12 @@
 import com.android.settingslib.media.LocalMediaManager
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.controls.util.MediaFlags
 import java.util.concurrent.Executor
 import javax.inject.Inject
 
 /** Factory class to create [MediaMuteAwaitConnectionManager] instances. */
 @SysUISingleton
 class MediaMuteAwaitConnectionManagerFactory @Inject constructor(
-    private val mediaFlags: MediaFlags,
     private val context: Context,
     private val logger: MediaMuteAwaitLogger,
     @Main private val mainExecutor: Executor
@@ -36,10 +34,7 @@
     private val deviceIconUtil = DeviceIconUtil()
 
     /** Creates a [MediaMuteAwaitConnectionManager]. */
-    fun create(localMediaManager: LocalMediaManager): MediaMuteAwaitConnectionManager? {
-        if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
-            return null
-        }
+    fun create(localMediaManager: LocalMediaManager): MediaMuteAwaitConnectionManager {
         return MediaMuteAwaitConnectionManager(
                 mainExecutor, localMediaManager, context, deviceIconUtil, logger
         )
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
index 60504e4..8a565fa 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
@@ -30,8 +30,4 @@
     /** Check whether the flag for the receiver success state is enabled. */
     fun isMediaTttReceiverSuccessRippleEnabled(): Boolean =
         featureFlags.isEnabled(Flags.MEDIA_TTT_RECEIVER_SUCCESS_RIPPLE)
-
-    /** True if the media transfer chip can be dismissed via a gesture. */
-    fun isMediaTttDismissGestureEnabled(): Boolean =
-        featureFlags.isEnabled(Flags.MEDIA_TAP_TO_TRANSFER_DISMISS_GESTURE)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index 3088d8b..11538fa 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -20,6 +20,7 @@
 import android.content.ComponentName
 import android.content.Context
 import android.os.UserHandle
+import androidx.lifecycle.DefaultLifecycleObserver
 import com.android.launcher3.icons.IconFactory
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.MediaProjectionAppSelectorActivity
@@ -46,6 +47,7 @@
 import dagger.Subcomponent
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import dagger.multibindings.IntoSet
 import javax.inject.Qualifier
 import javax.inject.Scope
 import kotlinx.coroutines.CoroutineScope
@@ -100,6 +102,12 @@
     @MediaProjectionAppSelectorScope
     fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader
 
+    @Binds
+    @IntoSet
+    fun taskPreviewSizeProviderAsLifecycleObserver(
+        impl: TaskPreviewSizeProvider
+    ): DefaultLifecycleObserver
+
     companion object {
         @Provides
         @MediaProjectionAppSelector
@@ -166,4 +174,5 @@
     @get:PersonalProfile val personalProfileUserHandle: UserHandle
 
     @MediaProjectionAppSelector val configurationController: ConfigurationController
+    val lifecycleObservers: Set<DefaultLifecycleObserver>
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index 89f66b7..864d35a 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -21,6 +21,8 @@
 import android.graphics.Rect
 import android.view.WindowInsets.Type
 import android.view.WindowManager
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
 import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
 import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
@@ -35,18 +37,22 @@
 constructor(
     private val context: Context,
     private val windowManager: WindowManager,
-    configurationController: ConfigurationController
-) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener {
+    private val configurationController: ConfigurationController,
+) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver {
 
     /** Returns the size of the task preview on the screen in pixels */
     val size: Rect = calculateSize()
 
     private val listeners = arrayListOf<TaskPreviewSizeListener>()
 
-    init {
+    override fun onCreate(owner: LifecycleOwner) {
         configurationController.addCallback(this)
     }
 
+    override fun onDestroy(owner: LifecycleOwner) {
+        configurationController.removeCallback(this)
+    }
+
     override fun onConfigChanged(newConfig: Configuration) {
         val newSize = calculateSize()
         if (newSize != size) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
index d652889..d949a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -145,13 +145,10 @@
     }
 
     interface Callback {
-        @JvmDefault
         fun onFlagMicCameraChanged(flag: Boolean) {}
 
-        @JvmDefault
         fun onFlagLocationChanged(flag: Boolean) {}
 
-        @JvmDefault
         fun onFlagMediaProjectionChanged(flag: Boolean) {}
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index a676150..eb8ef9b 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -216,7 +216,6 @@
     interface Callback : PrivacyConfig.Callback {
         fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
 
-        @JvmDefault
         fun onFlagAllChanged(flag: Boolean) {}
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java
deleted file mode 100644
index 08911d4..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyChipDrawable.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2021 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.privacy.television;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.Gravity;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.systemui.R;
-
-/**
- * Drawable that can go from being the background of the privacy icons to a small dot.
- * The icons are not included.
- */
-public class PrivacyChipDrawable extends Drawable {
-    private static final String TAG = PrivacyChipDrawable.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    private final Paint mChipPaint;
-    private final Paint mBgPaint;
-    private final Rect mTmpRect = new Rect();
-    private final Rect mBgRect = new Rect();
-    private final RectF mTmpRectF = new RectF();
-    private final Path mPath = new Path();
-    private final Animator mCollapse;
-    private final Animator mExpand;
-    private final int mLayoutDirection;
-    private final int mBgWidth;
-    private final int mBgHeight;
-    private final int mBgRadius;
-    private final int mDotSize;
-    private final float mExpandedChipRadius;
-    private final float mCollapsedChipRadius;
-
-    private final boolean mCollapseToDot;
-
-    private boolean mIsExpanded = true;
-    private float mCollapseProgress = 0f;
-
-    public PrivacyChipDrawable(Context context, int chipColorRes, boolean collapseToDot) {
-        mCollapseToDot = collapseToDot;
-
-        mChipPaint = new Paint();
-        mChipPaint.setStyle(Paint.Style.FILL);
-        mChipPaint.setColor(context.getColor(chipColorRes));
-        mChipPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-
-        mBgPaint = new Paint();
-        mBgPaint.setStyle(Paint.Style.FILL);
-        mBgPaint.setColor(context.getColor(R.color.privacy_chip_dot_bg_tint));
-        mBgPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
-
-        Resources res = context.getResources();
-        mLayoutDirection = res.getConfiguration().getLayoutDirection();
-        mBgWidth = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_width);
-        mBgHeight = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_height);
-        mBgRadius = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_bg_radius);
-        mDotSize = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_size);
-
-        mExpandedChipRadius = res.getDimensionPixelSize(R.dimen.privacy_chip_radius);
-        mCollapsedChipRadius = res.getDimensionPixelSize(R.dimen.privacy_chip_dot_radius);
-
-        mExpand = AnimatorInflater.loadAnimator(context, R.anim.tv_privacy_chip_expand);
-        mExpand.setTarget(this);
-        mCollapse = AnimatorInflater.loadAnimator(context, R.anim.tv_privacy_chip_collapse);
-        mCollapse.setTarget(this);
-    }
-
-    /**
-     * @return how far the chip is currently collapsed.
-     * @see #setCollapseProgress(float)
-     */
-    @Keep
-    public float getCollapseProgress() {
-        return mCollapseProgress;
-    }
-
-    /**
-     * Sets the collapsing progress of the chip to its collapsed state.
-     * @param pct How far the chip is collapsed, in the range 0-1.
-     *            0=fully expanded, 1=fully collapsed.
-     */
-    @Keep
-    public void setCollapseProgress(float pct) {
-        mCollapseProgress = pct;
-        invalidateSelf();
-    }
-
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        if (mCollapseProgress > 0f) {
-            // draw background
-            getBackgroundBounds(mBgRect);
-            mTmpRectF.set(mBgRect);
-            canvas.drawRoundRect(mTmpRectF, mBgRadius, mBgRadius, mBgPaint);
-        }
-
-        getForegroundBounds(mTmpRectF);
-        float radius = MathUtils.lerp(
-                mExpandedChipRadius,
-                mCollapseToDot ? mCollapsedChipRadius : mBgRadius,
-                mCollapseProgress);
-
-        canvas.drawRoundRect(mTmpRectF, radius, radius, mChipPaint);
-    }
-
-    private void getBackgroundBounds(Rect out) {
-        Rect bounds = getBounds();
-        Gravity.apply(Gravity.END, mBgWidth, mBgHeight, bounds, out, mLayoutDirection);
-    }
-
-    private void getCollapsedForegroundBounds(Rect out) {
-        Rect bounds = getBounds();
-        getBackgroundBounds(mBgRect);
-        if (mCollapseToDot) {
-            Gravity.apply(Gravity.CENTER, mDotSize, mDotSize, mBgRect, out);
-        } else {
-            out.set(bounds.left, mBgRect.top, bounds.right, mBgRect.bottom);
-        }
-    }
-
-    private void getForegroundBounds(RectF out) {
-        Rect bounds = getBounds();
-        getCollapsedForegroundBounds(mTmpRect);
-        lerpRect(bounds, mTmpRect, mCollapseProgress, out);
-    }
-
-    private void lerpRect(Rect start, Rect stop, float amount, RectF out) {
-        float left = MathUtils.lerp(start.left, stop.left, amount);
-        float top = MathUtils.lerp(start.top, stop.top, amount);
-        float right = MathUtils.lerp(start.right, stop.right, amount);
-        float bottom = MathUtils.lerp(start.bottom, stop.bottom, amount);
-        out.set(left, top, right, bottom);
-    }
-
-    /**
-     * Clips the given canvas to this chip's foreground shape.
-     * @param canvas Canvas to clip.
-     */
-    public void clipToForeground(Canvas canvas) {
-        getForegroundBounds(mTmpRectF);
-        float radius = MathUtils.lerp(
-                mExpandedChipRadius,
-                mCollapseToDot ? mCollapsedChipRadius : mBgRadius,
-                mCollapseProgress);
-
-        mPath.reset();
-        mPath.addRoundRect(mTmpRectF, radius, radius, Path.Direction.CW);
-        canvas.clipPath(mPath);
-    }
-
-    @Override
-    protected void onBoundsChange(@NonNull Rect bounds) {
-        super.onBoundsChange(bounds);
-        invalidateSelf();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mChipPaint.setAlpha(alpha);
-        mBgPaint.setAlpha(alpha);
-    }
-
-    /**
-     * Transitions to a full chip.
-     *
-     * @param animate Whether to animate the change to a full chip, or expand instantly.
-     */
-    public void expand(boolean animate) {
-        if (DEBUG) Log.d(TAG, "expanding");
-        if (mIsExpanded) {
-            return;
-        }
-        mIsExpanded = true;
-        if (animate) {
-            mCollapse.cancel();
-            mExpand.start();
-        } else {
-            mCollapseProgress = 0f;
-            invalidateSelf();
-        }
-    }
-
-    /**
-     * Starts the animation to a dot.
-     */
-    public void collapse() {
-        if (DEBUG) Log.d(TAG, "collapsing");
-        if (!mIsExpanded) {
-            return;
-        }
-        mIsExpanded = false;
-        mExpand.cancel();
-        mCollapse.start();
-    }
-
-    @Override
-    public void setColorFilter(@Nullable ColorFilter colorFilter) {
-        // no-op
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyItemsChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyItemsChip.java
deleted file mode 100644
index 158a591..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/PrivacyItemsChip.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.privacy.television;
-
-import android.annotation.ColorRes;
-import android.annotation.IntDef;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.drawable.Drawable;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.R;
-import com.android.systemui.privacy.PrivacyType;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-/**
- * View that shows indicator icons for privacy items.
- */
-public class PrivacyItemsChip extends FrameLayout {
-    private static final String TAG = "PrivacyItemsChip";
-    private static final boolean DEBUG = false;
-
-    /**
-     * Configuration for a PrivacyItemsChip's appearance.
-     */
-    public static class ChipConfig {
-        public final List<PrivacyType> privacyTypes;
-        @ColorRes
-        public final int colorRes;
-        public final boolean collapseToDot;
-
-        /**
-         * @param privacyTypes Privacy types to show icons for, in order.
-         * @param colorRes Color resource for the chip's foreground color.
-         * @param collapseToDot Whether to collapse the chip in to a dot,
-         *                      or just collapse it into a smaller chip with icons still visible.
-         */
-        public ChipConfig(@NonNull List<PrivacyType> privacyTypes, int colorRes,
-                boolean collapseToDot) {
-            this.privacyTypes = privacyTypes;
-            this.colorRes = colorRes;
-            this.collapseToDot = collapseToDot;
-        }
-    }
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"STATE_"}, value = {
-            STATE_NOT_SHOWN,
-            STATE_EXPANDED,
-            STATE_COLLAPSED,
-    })
-    public @interface State {
-    }
-
-    private static final int STATE_NOT_SHOWN = 0;
-    private static final int STATE_EXPANDED = 1;
-    private static final int STATE_COLLAPSED = 2;
-
-    private final ChipConfig mConfig;
-    private final int mIconSize;
-    private final int mCollapsedIconSize;
-    private final int mIconMarginHorizontal;
-    private final PrivacyChipDrawable mChipBackgroundDrawable;
-    private final List<ImageView> mIcons = new ArrayList<>();
-
-    @State
-    private int mState = STATE_NOT_SHOWN;
-
-    public PrivacyItemsChip(@NonNull Context context, @NonNull ChipConfig config) {
-        super(context);
-        mConfig = config;
-        setVisibility(View.GONE);
-
-        Resources res = context.getResources();
-        mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size);
-        mCollapsedIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_collapsed_icon_size);
-        mIconMarginHorizontal =
-                res.getDimensionPixelSize(R.dimen.privacy_chip_icon_margin_in_between);
-
-        LayoutInflater.from(context).inflate(R.layout.tv_ongoing_privacy_chip, this);
-        LinearLayout iconsContainer = findViewById(R.id.icons_container);
-
-        mChipBackgroundDrawable = new PrivacyChipDrawable(
-                context, config.colorRes, config.collapseToDot);
-        mChipBackgroundDrawable.setCallback(new Drawable.Callback() {
-            @Override
-            public void invalidateDrawable(@NonNull Drawable who) {
-                invalidate();
-            }
-
-            @Override
-            public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
-            }
-
-            @Override
-            public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
-            }
-        });
-
-        setBackground(mChipBackgroundDrawable);
-
-        for (PrivacyType type : config.privacyTypes) {
-            ImageView typeIconView = new ImageView(context);
-            Drawable icon = type.getIcon(context);
-            icon.mutate().setTint(context.getColor(R.color.privacy_icon_tint));
-
-            typeIconView.setImageDrawable(icon);
-            typeIconView.setScaleType(ImageView.ScaleType.FIT_CENTER);
-            mIcons.add(typeIconView);
-            iconsContainer.addView(typeIconView, mIconSize, mIconSize);
-            LinearLayout.LayoutParams lp =
-                    (LinearLayout.LayoutParams) typeIconView.getLayoutParams();
-            lp.leftMargin = mIconMarginHorizontal;
-            lp.rightMargin = mIconMarginHorizontal;
-            typeIconView.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Sets the active privacy types, and expands the chip if there are active items and the chip is
-     * currently collapsed, or hides the chip if there are no active items.
-     *
-     * @param types The set of active privacy types. Only types configured in {@link ChipConfig}
-     *              are shown.
-     */
-    public void expandForTypes(Set<PrivacyType> types) {
-        if (DEBUG) Log.d(TAG, "expandForTypes, state=" + stateToString(mState));
-
-        boolean hasActiveTypes = false;
-
-        for (int i = 0; i < mConfig.privacyTypes.size(); i++) {
-            PrivacyType type = mConfig.privacyTypes.get(i);
-            ImageView icon = mIcons.get(i);
-            boolean isTypeActive = types.contains(type);
-            hasActiveTypes = hasActiveTypes || isTypeActive;
-
-            icon.setVisibility(isTypeActive ? View.VISIBLE : View.GONE);
-
-            // Set icon size to expanded size
-            ViewGroup.LayoutParams lp = icon.getLayoutParams();
-            lp.width = mIconSize;
-            lp.height = mIconSize;
-            icon.requestLayout();
-        }
-
-        if (hasActiveTypes) {
-            if (DEBUG) Log.d(TAG, "Chip has active types, expanding");
-            if (mState == STATE_NOT_SHOWN) {
-                mChipBackgroundDrawable.expand(/* animate= */ false);
-            } else if (mState == STATE_COLLAPSED) {
-                mChipBackgroundDrawable.expand(/* animate= */ true);
-            }
-            setVisibility(View.VISIBLE);
-            setState(STATE_EXPANDED);
-        } else {
-            if (DEBUG) Log.d(TAG, "Chip has no active types, hiding");
-            setVisibility(View.GONE);
-            setState(STATE_NOT_SHOWN);
-        }
-    }
-
-    /**
-     * Collapses this chip if currently expanded.
-     */
-    public void collapse() {
-        if (DEBUG) Log.d(TAG, "collapse");
-
-        if (mState != STATE_EXPANDED) {
-            return;
-        }
-        setState(STATE_COLLAPSED);
-
-        for (ImageView icon : mIcons) {
-            if (mConfig.collapseToDot) {
-                icon.setVisibility(View.GONE);
-            } else {
-                ViewGroup.LayoutParams lp = icon.getLayoutParams();
-                lp.width = mCollapsedIconSize;
-                lp.height = mCollapsedIconSize;
-                icon.requestLayout();
-            }
-        }
-
-        mChipBackgroundDrawable.collapse();
-    }
-
-    public boolean isExpanded() {
-        return mState == STATE_EXPANDED;
-    }
-
-    private void setState(@State int state) {
-        if (mState != state) {
-            if (DEBUG) Log.d(TAG, "State changed: " + stateToString(state));
-            mState = state;
-        }
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        mChipBackgroundDrawable.clipToForeground(canvas);
-        super.dispatchDraw(canvas);
-    }
-
-    /**
-     * Used in debug logs.
-     */
-    private static String stateToString(@State int state) {
-        switch (state) {
-            case STATE_NOT_SHOWN:
-                return "NOT_SHOWN";
-            case STATE_EXPANDED:
-                return "EXPANDED";
-            case STATE_COLLAPSED:
-                return "COLLAPSED";
-            default:
-                return "INVALID";
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvPrivacyChipsController.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvPrivacyChipsController.java
deleted file mode 100644
index 5e4c797..0000000
--- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvPrivacyChipsController.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2021 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.privacy.television;
-
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UiThread;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.transition.AutoTransition;
-import android.transition.ChangeBounds;
-import android.transition.Fade;
-import android.transition.Transition;
-import android.transition.TransitionManager;
-import android.transition.TransitionSet;
-import android.util.ArraySet;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.IWindowManager;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.R;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.privacy.PrivacyItem;
-import com.android.systemui.privacy.PrivacyItemController;
-import com.android.systemui.privacy.PrivacyType;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import javax.inject.Inject;
-
-/**
- * A SystemUI component responsible for notifying the user whenever an application is
- * recording audio, camera, the screen, or accessing the location.
- */
-@SysUISingleton
-public class TvPrivacyChipsController 
-        implements CoreStartable, PrivacyItemController.Callback {
-    private static final String TAG = "TvPrivacyChipsController";
-    private static final boolean DEBUG = false;
-
-    // This title is used in CameraMicIndicatorsPermissionTest and
-    // RecognitionServiceMicIndicatorTest.
-    private static final String LAYOUT_PARAMS_TITLE = "MicrophoneCaptureIndicator";
-
-    // Chips configuration. We're not showing a location indicator on TV.
-    static final List<PrivacyItemsChip.ChipConfig> CHIPS = Arrays.asList(
-            new PrivacyItemsChip.ChipConfig(
-                    Collections.singletonList(PrivacyType.TYPE_MEDIA_PROJECTION),
-                    R.color.privacy_media_projection_chip,
-                    /* collapseToDot= */ false),
-            new PrivacyItemsChip.ChipConfig(
-                    Arrays.asList(PrivacyType.TYPE_CAMERA, PrivacyType.TYPE_MICROPHONE),
-                    R.color.privacy_mic_cam_chip,
-                    /* collapseToDot= */ true)
-    );
-
-    // Avoid multiple messages after rapid changes such as starting/stopping both camera and mic.
-    private static final int ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS = 500;
-
-    /**
-     * Time to collect privacy item updates before applying them.
-     * Since MediaProjection and AppOps come from different data sources,
-     * PrivacyItem updates when screen & audio recording ends do not come at the same time.
-     * Without this, if eg. MediaProjection ends first, you'd see the microphone chip expand and
-     * almost immediately fade out as it is expanding. With this, the two chips disappear together.
-     */
-    private static final int PRIVACY_ITEM_DEBOUNCE_TIMEOUT_MS = 200;
-
-    // How long chips stay expanded after an update.
-    private static final int EXPANDED_DURATION_MS = 4000;
-
-    private final Context mContext;
-    private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
-    private final Runnable mCollapseRunnable = this::collapseChips;
-    private final Runnable mUpdatePrivacyItemsRunnable = this::updateChipsAndAnnounce;
-    private final Runnable mAccessibilityRunnable = this::makeAccessibilityAnnouncement;
-
-    private final PrivacyItemController mPrivacyItemController;
-    private final IWindowManager mIWindowManager;
-    private final Rect[] mBounds = new Rect[4];
-    private final TransitionSet mTransition;
-    private final TransitionSet mCollapseTransition;
-    private boolean mIsRtl;
-
-    @Nullable
-    private ViewGroup mChipsContainer;
-    @Nullable
-    private List<PrivacyItemsChip> mChips;
-    @NonNull
-    private List<PrivacyItem> mPrivacyItems = Collections.emptyList();
-    @NonNull
-    private final List<PrivacyItem> mItemsBeforeLastAnnouncement = new ArrayList<>();
-
-    @Inject
-    public TvPrivacyChipsController(Context context, PrivacyItemController privacyItemController,
-            IWindowManager iWindowManager) {
-        mContext = context;
-        if (DEBUG) Log.d(TAG, "TvPrivacyChipsController running");
-        mPrivacyItemController = privacyItemController;
-        mIWindowManager = iWindowManager;
-
-        Resources res = mContext.getResources();
-        mIsRtl = res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        updateStaticPrivacyIndicatorBounds();
-
-        Interpolator collapseInterpolator = AnimationUtils.loadInterpolator(context,
-                R.interpolator.tv_privacy_chip_collapse_interpolator);
-        Interpolator expandInterpolator = AnimationUtils.loadInterpolator(context,
-                R.interpolator.tv_privacy_chip_expand_interpolator);
-
-        TransitionSet chipFadeTransition = new TransitionSet()
-                .addTransition(new Fade(Fade.IN))
-                .addTransition(new Fade(Fade.OUT));
-        chipFadeTransition.setOrdering(TransitionSet.ORDERING_TOGETHER);
-        chipFadeTransition.excludeTarget(ImageView.class, true);
-
-        Transition chipBoundsExpandTransition = new ChangeBounds();
-        chipBoundsExpandTransition.excludeTarget(ImageView.class, true);
-        chipBoundsExpandTransition.setInterpolator(expandInterpolator);
-
-        Transition chipBoundsCollapseTransition = new ChangeBounds();
-        chipBoundsCollapseTransition.excludeTarget(ImageView.class, true);
-        chipBoundsCollapseTransition.setInterpolator(collapseInterpolator);
-
-        TransitionSet iconCollapseTransition = new AutoTransition();
-        iconCollapseTransition.setOrdering(TransitionSet.ORDERING_TOGETHER);
-        iconCollapseTransition.addTarget(ImageView.class);
-        iconCollapseTransition.setInterpolator(collapseInterpolator);
-
-        TransitionSet iconExpandTransition = new AutoTransition();
-        iconExpandTransition.setOrdering(TransitionSet.ORDERING_TOGETHER);
-        iconExpandTransition.addTarget(ImageView.class);
-        iconExpandTransition.setInterpolator(expandInterpolator);
-
-        mTransition = new TransitionSet()
-                .addTransition(chipFadeTransition)
-                .addTransition(chipBoundsExpandTransition)
-                .addTransition(iconExpandTransition)
-                .setOrdering(TransitionSet.ORDERING_TOGETHER)
-                .setDuration(res.getInteger(R.integer.privacy_chip_animation_millis));
-
-        mCollapseTransition = new TransitionSet()
-                .addTransition(chipFadeTransition)
-                .addTransition(chipBoundsCollapseTransition)
-                .addTransition(iconCollapseTransition)
-                .setOrdering(TransitionSet.ORDERING_TOGETHER)
-                .setDuration(res.getInteger(R.integer.privacy_chip_animation_millis));
-
-        Transition.TransitionListener transitionListener = new Transition.TransitionListener() {
-            @Override
-            public void onTransitionStart(Transition transition) {
-                if (DEBUG) Log.v(TAG, "onTransitionStart");
-            }
-
-            @Override
-            public void onTransitionEnd(Transition transition) {
-                if (DEBUG) Log.v(TAG, "onTransitionEnd");
-                if (mChips != null) {
-                    boolean hasVisibleChip = false;
-                    boolean hasExpandedChip = false;
-                    for (PrivacyItemsChip chip : mChips) {
-                        hasVisibleChip = hasVisibleChip || chip.getVisibility() == View.VISIBLE;
-                        hasExpandedChip = hasExpandedChip || chip.isExpanded();
-                    }
-
-                    if (!hasVisibleChip) {
-                        if (DEBUG) Log.d(TAG, "No chips visible anymore");
-                        removeIndicatorView();
-                    } else if (hasExpandedChip) {
-                        if (DEBUG) Log.d(TAG, "Has expanded chips");
-                        collapseLater();
-                    }
-                }
-            }
-
-            @Override
-            public void onTransitionCancel(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionPause(Transition transition) {
-            }
-
-            @Override
-            public void onTransitionResume(Transition transition) {
-            }
-        };
-
-        mTransition.addListener(transitionListener);
-        mCollapseTransition.addListener(transitionListener);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration config) {
-        boolean updatedRtl = config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        if (mIsRtl == updatedRtl) {
-            return;
-        }
-        mIsRtl = updatedRtl;
-
-        // Update privacy chip location.
-        if (mChipsContainer != null) {
-            removeIndicatorView();
-            createAndShowIndicator();
-        }
-        updateStaticPrivacyIndicatorBounds();
-    }
-
-    @Override
-    public void start() {
-        mPrivacyItemController.addCallback(this);
-    }
-
-    @UiThread
-    @Override
-    public void onPrivacyItemsChanged(List<PrivacyItem> privacyItems) {
-        if (DEBUG) Log.d(TAG, "onPrivacyItemsChanged");
-
-        List<PrivacyItem> filteredPrivacyItems = new ArrayList<>(privacyItems);
-        if (filteredPrivacyItems.removeIf(
-                privacyItem -> !isPrivacyTypeShown(privacyItem.getPrivacyType()))) {
-            if (DEBUG) Log.v(TAG, "Removed privacy items we don't show");
-        }
-
-        // Do they have the same elements? (order doesn't matter)
-        if (privacyItems.size() == mPrivacyItems.size() && mPrivacyItems.containsAll(
-                privacyItems)) {
-            if (DEBUG) Log.d(TAG, "No change to relevant privacy items");
-            return;
-        }
-
-        mPrivacyItems = privacyItems;
-
-        if (!mUiThreadHandler.hasCallbacks(mUpdatePrivacyItemsRunnable)) {
-            mUiThreadHandler.postDelayed(mUpdatePrivacyItemsRunnable,
-                    PRIVACY_ITEM_DEBOUNCE_TIMEOUT_MS);
-        }
-    }
-
-    private boolean isPrivacyTypeShown(@NonNull PrivacyType type) {
-        for (PrivacyItemsChip.ChipConfig chip : CHIPS) {
-            if (chip.privacyTypes.contains(type)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @UiThread
-    private void updateChipsAndAnnounce() {
-        updateChips();
-        postAccessibilityAnnouncement();
-    }
-
-    private void updateStaticPrivacyIndicatorBounds() {
-        Resources res = mContext.getResources();
-        int mMaxExpandedWidth = res.getDimensionPixelSize(R.dimen.privacy_chips_max_width);
-        int mMaxExpandedHeight = res.getDimensionPixelSize(R.dimen.privacy_chip_height);
-        int mChipMarginTotal = 2 * res.getDimensionPixelSize(R.dimen.privacy_chip_margin);
-
-        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        Rect screenBounds = windowManager.getCurrentWindowMetrics().getBounds();
-        mBounds[0] = new Rect(
-                mIsRtl ? screenBounds.left
-                        : screenBounds.right - mMaxExpandedWidth,
-                screenBounds.top,
-                mIsRtl ? screenBounds.left + mMaxExpandedWidth
-                        : screenBounds.right,
-                screenBounds.top + mChipMarginTotal + mMaxExpandedHeight
-        );
-
-        if (DEBUG) Log.v(TAG, "privacy indicator bounds: " + mBounds[0].toShortString());
-
-        try {
-            mIWindowManager.updateStaticPrivacyIndicatorBounds(mContext.getDisplayId(), mBounds);
-        } catch (RemoteException e) {
-            Log.w(TAG, "could not update privacy indicator bounds");
-        }
-    }
-
-    @UiThread
-    private void updateChips() {
-        if (DEBUG) Log.d(TAG, "updateChips: " + mPrivacyItems.size() + " privacy items");
-
-        if (mChipsContainer == null) {
-            if (!mPrivacyItems.isEmpty()) {
-                createAndShowIndicator();
-            }
-            return;
-        }
-
-        Set<PrivacyType> activePrivacyTypes = new ArraySet<>();
-        mPrivacyItems.forEach(item -> activePrivacyTypes.add(item.getPrivacyType()));
-
-        TransitionManager.beginDelayedTransition(mChipsContainer, mTransition);
-        mChips.forEach(chip -> chip.expandForTypes(activePrivacyTypes));
-    }
-
-    /**
-     * Collapse the chip {@link #EXPANDED_DURATION_MS} from now.
-     */
-    private void collapseLater() {
-        mUiThreadHandler.removeCallbacks(mCollapseRunnable);
-        if (DEBUG) Log.d(TAG, "Chips will collapse in " + EXPANDED_DURATION_MS + "ms");
-        mUiThreadHandler.postDelayed(mCollapseRunnable, EXPANDED_DURATION_MS);
-    }
-
-    private void collapseChips() {
-        if (DEBUG) Log.d(TAG, "collapseChips");
-        if (mChipsContainer == null) {
-            return;
-        }
-
-        boolean hasExpandedChip = false;
-        for (PrivacyItemsChip chip : mChips) {
-            hasExpandedChip |= chip.isExpanded();
-        }
-
-        if (mChipsContainer != null && hasExpandedChip) {
-            TransitionManager.beginDelayedTransition(mChipsContainer, mCollapseTransition);
-            for (PrivacyItemsChip chip : mChips) {
-                chip.collapse();
-            }
-        }
-    }
-
-    @UiThread
-    private void createAndShowIndicator() {
-        if (DEBUG) Log.i(TAG, "Creating privacy indicators");
-
-        Context privacyChipContext = new ContextThemeWrapper(mContext, R.style.PrivacyChip);
-        mChips = new ArrayList<>();
-        mChipsContainer = (ViewGroup) LayoutInflater.from(privacyChipContext)
-                .inflate(R.layout.tv_privacy_chip_container, null);
-
-        int chipMargins = privacyChipContext.getResources()
-                .getDimensionPixelSize(R.dimen.privacy_chip_margin);
-        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
-        lp.setMarginStart(chipMargins);
-        lp.setMarginEnd(chipMargins);
-
-        for (PrivacyItemsChip.ChipConfig chipConfig : CHIPS) {
-            PrivacyItemsChip chip = new PrivacyItemsChip(privacyChipContext, chipConfig);
-            mChipsContainer.addView(chip, lp);
-            mChips.add(chip);
-        }
-
-        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        windowManager.addView(mChipsContainer, getWindowLayoutParams());
-
-        final ViewGroup container = mChipsContainer;
-        mChipsContainer.getViewTreeObserver()
-                .addOnGlobalLayoutListener(
-                        new ViewTreeObserver.OnGlobalLayoutListener() {
-                            @Override
-                            public void onGlobalLayout() {
-                                if (DEBUG) Log.v(TAG, "Chips container laid out");
-                                container.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                                updateChips();
-                            }
-                        });
-    }
-
-    private WindowManager.LayoutParams getWindowLayoutParams() {
-        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
-                WRAP_CONTENT,
-                WRAP_CONTENT,
-                WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                PixelFormat.TRANSLUCENT);
-        layoutParams.gravity = Gravity.TOP | (mIsRtl ? Gravity.LEFT : Gravity.RIGHT);
-        layoutParams.setTitle(LAYOUT_PARAMS_TITLE);
-        layoutParams.packageName = mContext.getPackageName();
-        return layoutParams;
-    }
-
-    @UiThread
-    private void removeIndicatorView() {
-        if (DEBUG) Log.d(TAG, "removeIndicatorView");
-        mUiThreadHandler.removeCallbacks(mCollapseRunnable);
-
-        final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
-        if (windowManager != null && mChipsContainer != null) {
-            windowManager.removeView(mChipsContainer);
-        }
-
-        mChipsContainer = null;
-        mChips = null;
-    }
-
-    /**
-     * Schedules the accessibility announcement to be made after {@link
-     * #ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS} (if possible). This is so that only one announcement is
-     * made instead of two separate ones if both the camera and the mic are started/stopped.
-     */
-    @UiThread
-    private void postAccessibilityAnnouncement() {
-        mUiThreadHandler.removeCallbacks(mAccessibilityRunnable);
-
-        if (mPrivacyItems.size() == 0) {
-            // Announce immediately since announcement cannot be made once the chip is gone.
-            makeAccessibilityAnnouncement();
-        } else {
-            mUiThreadHandler.postDelayed(mAccessibilityRunnable,
-                    ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS);
-        }
-    }
-
-    private void makeAccessibilityAnnouncement() {
-        if (mChipsContainer == null) {
-            return;
-        }
-
-        boolean cameraWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement,
-                PrivacyType.TYPE_CAMERA);
-        boolean cameraIsRecording = listContainsPrivacyType(mPrivacyItems,
-                PrivacyType.TYPE_CAMERA);
-        boolean micWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement,
-                PrivacyType.TYPE_MICROPHONE);
-        boolean micIsRecording = listContainsPrivacyType(mPrivacyItems,
-                PrivacyType.TYPE_MICROPHONE);
-
-        boolean screenWasRecording = listContainsPrivacyType(mItemsBeforeLastAnnouncement,
-                PrivacyType.TYPE_MEDIA_PROJECTION);
-        boolean screenIsRecording = listContainsPrivacyType(mPrivacyItems,
-                PrivacyType.TYPE_MEDIA_PROJECTION);
-
-        int announcement = 0;
-        if (!cameraWasRecording && cameraIsRecording && !micWasRecording && micIsRecording) {
-            // Both started
-            announcement = R.string.mic_and_camera_recording_announcement;
-        } else if (cameraWasRecording && !cameraIsRecording && micWasRecording && !micIsRecording) {
-            // Both stopped
-            announcement = R.string.mic_camera_stopped_recording_announcement;
-        } else {
-            // Did the camera start or stop?
-            if (cameraWasRecording && !cameraIsRecording) {
-                announcement = R.string.camera_stopped_recording_announcement;
-            } else if (!cameraWasRecording && cameraIsRecording) {
-                announcement = R.string.camera_recording_announcement;
-            }
-
-            // Announce camera changes now since we might need a second announcement about the mic.
-            if (announcement != 0) {
-                mChipsContainer.announceForAccessibility(mContext.getString(announcement));
-                announcement = 0;
-            }
-
-            // Did the mic start or stop?
-            if (micWasRecording && !micIsRecording) {
-                announcement = R.string.mic_stopped_recording_announcement;
-            } else if (!micWasRecording && micIsRecording) {
-                announcement = R.string.mic_recording_announcement;
-            }
-        }
-
-        if (announcement != 0) {
-            mChipsContainer.announceForAccessibility(mContext.getString(announcement));
-        }
-
-        if (!screenWasRecording && screenIsRecording) {
-            mChipsContainer.announceForAccessibility(
-                    mContext.getString(R.string.screen_recording_announcement));
-        } else if (screenWasRecording && !screenIsRecording) {
-            mChipsContainer.announceForAccessibility(
-                    mContext.getString(R.string.screen_stopped_recording_announcement));
-        }
-
-        mItemsBeforeLastAnnouncement.clear();
-        mItemsBeforeLastAnnouncement.addAll(mPrivacyItems);
-    }
-
-    private boolean listContainsPrivacyType(List<PrivacyItem> list, PrivacyType privacyType) {
-        for (PrivacyItem item : list) {
-            if (item.getPrivacyType() == privacyType) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 995c6a4..33c47cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -26,7 +26,7 @@
 import javax.inject.Inject
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
+import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
 import com.android.systemui.statusbar.policy.DeviceProvisionedController
 import javax.inject.Named
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ChevronImageView.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ChevronImageView.kt
new file mode 100644
index 0000000..8fd1d6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/ChevronImageView.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.tileimpl
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+
+class ChevronImageView(context: Context, attrs: AttributeSet?) : ImageView(context, attrs) {
+
+    override fun resolveLayoutDirection(): Boolean {
+        val previousLayoutDirection = layoutDirection
+        return super.resolveLayoutDirection().also { resolved ->
+            if (resolved && layoutDirection != previousLayoutDirection) {
+                onRtlPropertiesChanged(layoutDirection)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
index 5aa5fee..f9324a9 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt
@@ -16,19 +16,25 @@
 
 package com.android.systemui.scene.ui.view
 
+import android.view.Gravity
+import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
+import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
 import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.SceneKey
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import java.time.Instant
 import kotlinx.coroutines.launch
 
 object SceneWindowRootViewBinder {
@@ -77,6 +83,9 @@
                         )
                     )
 
+                    val legacyView = view.requireViewById<View>(R.id.legacy_window_root)
+                    view.addView(createVisibilityToggleView(legacyView))
+
                     launch {
                         viewModel.isVisible.collect { isVisible ->
                             onVisibilityChangedInternal(isVisible)
@@ -89,4 +98,28 @@
             }
         }
     }
+
+    private var clickCount = 0
+    private var lastClick = Instant.now()
+
+    /**
+     * A temporary UI to toggle on/off the visibility of the given [otherView]. It is toggled by
+     * tapping 5 times in quick succession on the device camera (top center).
+     */
+    // TODO(b/291321285): Remove this when the Flexiglass UI is mature enough to turn off legacy
+    //  SysUI altogether.
+    private fun createVisibilityToggleView(otherView: View): View {
+        val toggleView = View(otherView.context)
+        toggleView.layoutParams = FrameLayout.LayoutParams(200, 200, Gravity.CENTER_HORIZONTAL)
+        toggleView.setOnClickListener {
+            val now = Instant.now()
+            clickCount = if (now.minusSeconds(2) > lastClick) 1 else clickCount + 1
+            if (clickCount == 5) {
+                otherView.isVisible = !otherView.isVisible
+                clickCount = 0
+            }
+            lastClick = now
+        }
+        return toggleView
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvSensorPrivacyChangedActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvSensorPrivacyChangedActivity.java
deleted file mode 100644
index 731b177..0000000
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvSensorPrivacyChangedActivity.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.sensorprivacy.television;
-
-import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
-import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-
-import android.annotation.DimenRes;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.hardware.SensorPrivacyManager;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
-import com.android.systemui.tv.TvBottomSheetActivity;
-import com.android.systemui.util.settings.GlobalSettings;
-
-import javax.inject.Inject;
-
-/**
- * Bottom sheet that is shown when the camera/mic sensors privacy state changed
- * by the global software toggle or physical privacy switch.
- */
-public class TvSensorPrivacyChangedActivity extends TvBottomSheetActivity {
-
-    private static final String TAG = TvSensorPrivacyChangedActivity.class.getSimpleName();
-
-    private static final int ALL_SENSORS = Integer.MAX_VALUE;
-
-    private int mSensor = -1;
-    private int mToggleType = -1;
-
-    private final GlobalSettings mGlobalSettings;
-    private final IndividualSensorPrivacyController mSensorPrivacyController;
-    private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
-    private TextView mTitle;
-    private TextView mContent;
-    private ImageView mIcon;
-    private ImageView mSecondIcon;
-    private Button mPositiveButton;
-    private Button mCancelButton;
-
-    @Inject
-    public TvSensorPrivacyChangedActivity(
-            IndividualSensorPrivacyController individualSensorPrivacyController,
-            GlobalSettings globalSettings) {
-        mSensorPrivacyController = individualSensorPrivacyController;
-        mGlobalSettings = globalSettings;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addSystemFlags(
-                WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
-        boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
-                false);
-        if (allSensors) {
-            mSensor = ALL_SENSORS;
-        } else {
-            mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
-        }
-
-        mToggleType = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_TOGGLE_TYPE, -1);
-
-        if (mSensor == -1 || mToggleType == -1) {
-            Log.v(TAG, "Invalid extras");
-            finish();
-            return;
-        }
-
-        // Do not show for software toggles
-        if (mToggleType == SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE) {
-            finish();
-            return;
-        }
-
-        mSensorPrivacyCallback = (sensor, blocked) -> {
-            updateUI();
-        };
-
-        initUI();
-    }
-
-    private void initUI() {
-        mTitle = findViewById(R.id.bottom_sheet_title);
-        mContent = findViewById(R.id.bottom_sheet_body);
-        mIcon = findViewById(R.id.bottom_sheet_icon);
-        // mic icon if both icons are shown
-        mSecondIcon = findViewById(R.id.bottom_sheet_second_icon);
-        mPositiveButton = findViewById(R.id.bottom_sheet_positive_button);
-        mCancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
-        mCancelButton.setText(android.R.string.cancel);
-        mCancelButton.setOnClickListener(v -> finish());
-
-        updateUI();
-    }
-
-    private void updateUI() {
-        final Resources resources = getResources();
-        setIconTint(resources.getBoolean(R.bool.config_unblockHwSensorIconEnableTint));
-        setIconSize(R.dimen.unblock_hw_sensor_icon_width, R.dimen.unblock_hw_sensor_icon_height);
-
-        switch (mSensor) {
-            case CAMERA:
-                updateUiForCameraUpdate(
-                        mSensorPrivacyController.isSensorBlockedByHardwareToggle(CAMERA));
-                break;
-            case MICROPHONE:
-            default:
-                updateUiForMicUpdate(
-                        mSensorPrivacyController.isSensorBlockedByHardwareToggle(MICROPHONE));
-                break;
-        }
-
-        // Start animation if drawable is animated
-        Drawable iconDrawable = mIcon.getDrawable();
-        if (iconDrawable instanceof Animatable) {
-            ((Animatable) iconDrawable).start();
-        }
-
-        mPositiveButton.setVisibility(View.GONE);
-        mCancelButton.setText(android.R.string.ok);
-    }
-
-    private void updateUiForMicUpdate(boolean blocked) {
-        if (blocked) {
-            mTitle.setText(R.string.sensor_privacy_mic_turned_off_dialog_title);
-            if (isExplicitUserInteractionAudioBypassAllowed()) {
-                mContent.setText(R.string.sensor_privacy_mic_blocked_with_exception_dialog_content);
-            } else {
-                mContent.setText(R.string.sensor_privacy_mic_blocked_no_exception_dialog_content);
-            }
-            mIcon.setImageResource(R.drawable.unblock_hw_sensor_microphone);
-            mSecondIcon.setVisibility(View.GONE);
-        } else {
-            mTitle.setText(R.string.sensor_privacy_mic_turned_on_dialog_title);
-            mContent.setText(R.string.sensor_privacy_mic_unblocked_dialog_content);
-            mIcon.setImageResource(com.android.internal.R.drawable.ic_mic_allowed);
-            mSecondIcon.setVisibility(View.GONE);
-        }
-    }
-
-    private void updateUiForCameraUpdate(boolean blocked) {
-        if (blocked) {
-            mTitle.setText(R.string.sensor_privacy_camera_turned_off_dialog_title);
-            mContent.setText(R.string.sensor_privacy_camera_blocked_dialog_content);
-            mIcon.setImageResource(R.drawable.unblock_hw_sensor_camera);
-            mSecondIcon.setVisibility(View.GONE);
-        } else {
-            mTitle.setText(R.string.sensor_privacy_camera_turned_on_dialog_title);
-            mContent.setText(R.string.sensor_privacy_camera_unblocked_dialog_content);
-            mIcon.setImageResource(com.android.internal.R.drawable.ic_camera_allowed);
-            mSecondIcon.setVisibility(View.GONE);
-        }
-    }
-
-    private void setIconTint(boolean enableTint) {
-        final Resources resources = getResources();
-
-        if (enableTint) {
-            final ColorStateList iconTint = resources.getColorStateList(
-                    R.color.bottom_sheet_icon_color, getTheme());
-            mIcon.setImageTintList(iconTint);
-            mSecondIcon.setImageTintList(iconTint);
-        } else {
-            mIcon.setImageTintList(null);
-            mSecondIcon.setImageTintList(null);
-        }
-
-        mIcon.invalidate();
-        mSecondIcon.invalidate();
-    }
-
-    private void setIconSize(@DimenRes int widthRes, @DimenRes int heightRes) {
-        final Resources resources = getResources();
-        final int iconWidth = resources.getDimensionPixelSize(widthRes);
-        final int iconHeight = resources.getDimensionPixelSize(heightRes);
-
-        mIcon.getLayoutParams().width = iconWidth;
-        mIcon.getLayoutParams().height = iconHeight;
-        mIcon.invalidate();
-
-        mSecondIcon.getLayoutParams().width = iconWidth;
-        mSecondIcon.getLayoutParams().height = iconHeight;
-        mSecondIcon.invalidate();
-    }
-
-    private boolean isExplicitUserInteractionAudioBypassAllowed() {
-        return mGlobalSettings.getInt(
-                Settings.Global.RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO_ENABLED, 1) == 1;
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateUI();
-        mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
-    }
-
-    @Override
-    public void onPause() {
-        mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
-        super.onPause();
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java b/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
deleted file mode 100644
index 1b9657f..0000000
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/television/TvUnblockSensorActivity.java
+++ /dev/null
@@ -1,342 +0,0 @@
-/*
- * Copyright (C) 2021 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.sensorprivacy.television;
-
-import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
-import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
-import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
-import static android.hardware.SensorPrivacyManager.Sources.OTHER;
-
-import android.annotation.DimenRes;
-import android.app.AppOpsManager;
-import android.app.role.RoleManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.Drawable;
-import android.hardware.SensorPrivacyManager;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
-import com.android.systemui.tv.TvBottomSheetActivity;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-/**
- * Bottom sheet that is shown when the camera/mic sensors are blocked by the global software toggle
- * or physical privacy switch.
- */
-public class TvUnblockSensorActivity extends TvBottomSheetActivity {
-
-    private static final String TAG = TvUnblockSensorActivity.class.getSimpleName();
-    private static final String ACTION_MANAGE_CAMERA_PRIVACY =
-            "android.settings.MANAGE_CAMERA_PRIVACY";
-    private static final String ACTION_MANAGE_MICROPHONE_PRIVACY =
-            "android.settings.MANAGE_MICROPHONE_PRIVACY";
-
-    private static final int ALL_SENSORS = Integer.MAX_VALUE;
-
-    private int mSensor = -1;
-
-    private final AppOpsManager mAppOpsManager;
-    private final RoleManager mRoleManager;
-    private final IndividualSensorPrivacyController mSensorPrivacyController;
-    private IndividualSensorPrivacyController.Callback mSensorPrivacyCallback;
-    private TextView mTitle;
-    private TextView mContent;
-    private ImageView mIcon;
-    private ImageView mSecondIcon;
-    private Button mPositiveButton;
-    private Button mCancelButton;
-
-    @Inject
-    public TvUnblockSensorActivity(
-            IndividualSensorPrivacyController individualSensorPrivacyController,
-            AppOpsManager appOpsManager, RoleManager roleManager) {
-        mSensorPrivacyController = individualSensorPrivacyController;
-        mAppOpsManager = appOpsManager;
-        mRoleManager = roleManager;
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        getWindow().addSystemFlags(
-                WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-
-        boolean allSensors = getIntent().getBooleanExtra(SensorPrivacyManager.EXTRA_ALL_SENSORS,
-                false);
-        if (allSensors) {
-            mSensor = ALL_SENSORS;
-        } else {
-            mSensor = getIntent().getIntExtra(SensorPrivacyManager.EXTRA_SENSOR, -1);
-        }
-
-        if (mSensor == -1) {
-            Log.v(TAG, "Invalid extras");
-            finish();
-            return;
-        }
-
-        mSensorPrivacyCallback = (sensor, blocked) -> {
-            if (mSensor == ALL_SENSORS && !mSensorPrivacyController.isSensorBlocked(CAMERA)
-                    && !mSensorPrivacyController.isSensorBlocked(MICROPHONE)) {
-                showToastAndFinish();
-            } else if (this.mSensor == sensor && !blocked) {
-                showToastAndFinish();
-            } else {
-                updateUI();
-            }
-        };
-
-        initUI();
-    }
-
-    private void showToastAndFinish() {
-        final int toastMsgResId;
-        switch(mSensor) {
-            case MICROPHONE:
-                toastMsgResId = R.string.sensor_privacy_mic_unblocked_toast_content;
-                break;
-            case CAMERA:
-                toastMsgResId = R.string.sensor_privacy_camera_unblocked_toast_content;
-                break;
-            case ALL_SENSORS:
-            default:
-                toastMsgResId = R.string.sensor_privacy_mic_camera_unblocked_toast_content;
-                break;
-        }
-        showToastAndFinish(toastMsgResId);
-    }
-
-    private void showToastAndFinish(int toastMsgResId) {
-        Toast.makeText(this, toastMsgResId, Toast.LENGTH_SHORT).show();
-        finish();
-    }
-
-    private boolean isBlockedByHardwareToggle() {
-        if (mSensor == ALL_SENSORS) {
-            return mSensorPrivacyController.isSensorBlockedByHardwareToggle(CAMERA)
-                    || mSensorPrivacyController.isSensorBlockedByHardwareToggle(MICROPHONE);
-        } else {
-            return mSensorPrivacyController.isSensorBlockedByHardwareToggle(mSensor);
-        }
-    }
-
-    private void initUI() {
-        mTitle = findViewById(R.id.bottom_sheet_title);
-        mContent = findViewById(R.id.bottom_sheet_body);
-        mIcon = findViewById(R.id.bottom_sheet_icon);
-        // mic icon if both icons are shown
-        mSecondIcon = findViewById(R.id.bottom_sheet_second_icon);
-        mPositiveButton = findViewById(R.id.bottom_sheet_positive_button);
-        mCancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
-        mCancelButton.setText(android.R.string.cancel);
-        mCancelButton.setOnClickListener(v -> finish());
-
-        updateUI();
-    }
-
-    private void updateUI() {
-        if (isHTTAccessDisabled()) {
-            updateUiForHTT();
-        } else if (isBlockedByHardwareToggle()) {
-            updateUiForHardwareToggle();
-        } else {
-            updateUiForSoftwareToggle();
-        }
-    }
-
-    private void updateUiForHardwareToggle() {
-        final Resources resources = getResources();
-
-        boolean micBlocked = (mSensor == MICROPHONE || mSensor == ALL_SENSORS)
-                && mSensorPrivacyController.isSensorBlockedByHardwareToggle(MICROPHONE);
-        boolean cameraBlocked = (mSensor == CAMERA || mSensor == ALL_SENSORS)
-                && mSensorPrivacyController.isSensorBlockedByHardwareToggle(CAMERA);
-
-        setIconTint(resources.getBoolean(R.bool.config_unblockHwSensorIconEnableTint));
-        setIconSize(R.dimen.unblock_hw_sensor_icon_width, R.dimen.unblock_hw_sensor_icon_height);
-
-        if (micBlocked && cameraBlocked) {
-            mTitle.setText(R.string.sensor_privacy_start_use_mic_camera_blocked_dialog_title);
-            mContent.setText(
-                    R.string.sensor_privacy_start_use_mic_camera_blocked_dialog_content);
-            mIcon.setImageResource(R.drawable.unblock_hw_sensor_all);
-
-            Drawable secondIconDrawable = resources.getDrawable(
-                    R.drawable.unblock_hw_sensor_all_second, getTheme());
-            if (secondIconDrawable == null) {
-                mSecondIcon.setVisibility(View.GONE);
-            } else {
-                mSecondIcon.setImageDrawable(secondIconDrawable);
-            }
-        } else if (cameraBlocked) {
-            mTitle.setText(R.string.sensor_privacy_start_use_camera_blocked_dialog_title);
-            mContent.setText(R.string.sensor_privacy_start_use_camera_blocked_dialog_content);
-            mIcon.setImageResource(R.drawable.unblock_hw_sensor_camera);
-            mSecondIcon.setVisibility(View.GONE);
-        } else if (micBlocked) {
-            mTitle.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_title);
-            mContent.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_content);
-            mIcon.setImageResource(R.drawable.unblock_hw_sensor_microphone);
-            mSecondIcon.setVisibility(View.GONE);
-        }
-
-        // Start animation if drawable is animated
-        Drawable iconDrawable = mIcon.getDrawable();
-        if (iconDrawable instanceof Animatable) {
-            ((Animatable) iconDrawable).start();
-        }
-
-        mPositiveButton.setVisibility(View.GONE);
-        mCancelButton.setText(android.R.string.ok);
-    }
-
-    private void updateUiForSoftwareToggle() {
-        setIconTint(true);
-        setIconSize(R.dimen.bottom_sheet_icon_size, R.dimen.bottom_sheet_icon_size);
-
-        switch (mSensor) {
-            case MICROPHONE:
-                mTitle.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_title);
-                mContent.setText(R.string.sensor_privacy_start_use_mic_dialog_content);
-                mIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
-                mSecondIcon.setVisibility(View.GONE);
-                break;
-            case CAMERA:
-                mTitle.setText(R.string.sensor_privacy_start_use_camera_blocked_dialog_title);
-                mContent.setText(R.string.sensor_privacy_start_use_camera_dialog_content);
-                mIcon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
-                mSecondIcon.setVisibility(View.GONE);
-                break;
-            case ALL_SENSORS:
-            default:
-                mTitle.setText(R.string.sensor_privacy_start_use_mic_camera_blocked_dialog_title);
-                mContent.setText(R.string.sensor_privacy_start_use_mic_camera_dialog_content);
-                mIcon.setImageResource(com.android.internal.R.drawable.perm_group_camera);
-                mSecondIcon.setImageResource(
-                        com.android.internal.R.drawable.perm_group_microphone);
-                break;
-        }
-
-        mPositiveButton.setText(
-                com.android.internal.R.string.sensor_privacy_start_use_dialog_turn_on_button);
-        mPositiveButton.setOnClickListener(v -> {
-            if (mSensor == ALL_SENSORS) {
-                mSensorPrivacyController.setSensorBlocked(OTHER, CAMERA, false);
-                mSensorPrivacyController.setSensorBlocked(OTHER, MICROPHONE, false);
-            } else {
-                mSensorPrivacyController.setSensorBlocked(OTHER, mSensor, false);
-            }
-        });
-    }
-
-    private void updateUiForHTT() {
-        setIconTint(true);
-        setIconSize(R.dimen.bottom_sheet_icon_size, R.dimen.bottom_sheet_icon_size);
-
-        mTitle.setText(R.string.sensor_privacy_start_use_mic_blocked_dialog_title);
-        mContent.setText(R.string.sensor_privacy_htt_blocked_dialog_content);
-        mIcon.setImageResource(com.android.internal.R.drawable.perm_group_microphone);
-        mSecondIcon.setVisibility(View.GONE);
-
-        mPositiveButton.setText(R.string.sensor_privacy_dialog_open_settings);
-        mPositiveButton.setOnClickListener(v -> {
-            Intent openPrivacySettings = new Intent(ACTION_MANAGE_MICROPHONE_PRIVACY);
-            ActivityInfo activityInfo = openPrivacySettings.resolveActivityInfo(getPackageManager(),
-                    MATCH_SYSTEM_ONLY);
-            if (activityInfo == null) {
-                showToastAndFinish(com.android.internal.R.string.noApplications);
-            } else {
-                startActivity(openPrivacySettings);
-                finish();
-            }
-        });
-    }
-
-    private void setIconTint(boolean enableTint) {
-        final Resources resources = getResources();
-
-        if (enableTint) {
-            final ColorStateList iconTint = resources.getColorStateList(
-                    R.color.bottom_sheet_icon_color, getTheme());
-            mIcon.setImageTintList(iconTint);
-            mSecondIcon.setImageTintList(iconTint);
-        } else {
-            mIcon.setImageTintList(null);
-            mSecondIcon.setImageTintList(null);
-        }
-
-        mIcon.invalidate();
-        mSecondIcon.invalidate();
-    }
-
-    private void setIconSize(@DimenRes int widthRes, @DimenRes int heightRes) {
-        final Resources resources = getResources();
-        final int iconWidth = resources.getDimensionPixelSize(widthRes);
-        final int iconHeight = resources.getDimensionPixelSize(heightRes);
-
-        mIcon.getLayoutParams().width = iconWidth;
-        mIcon.getLayoutParams().height = iconHeight;
-        mIcon.invalidate();
-
-        mSecondIcon.getLayoutParams().width = iconWidth;
-        mSecondIcon.getLayoutParams().height = iconHeight;
-        mSecondIcon.invalidate();
-    }
-
-    private boolean isHTTAccessDisabled() {
-        String pkg = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
-        List<String> assistantPkgs = mRoleManager.getRoleHolders(RoleManager.ROLE_ASSISTANT);
-        if (!assistantPkgs.contains(pkg)) {
-            return false;
-        }
-
-        return (mAppOpsManager.checkOpNoThrow(
-                AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO, UserHandle.myUserId(),
-                pkg) != AppOpsManager.MODE_ALLOWED);
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateUI();
-        mSensorPrivacyController.addCallback(mSensorPrivacyCallback);
-    }
-
-    @Override
-    public void onPause() {
-        mSensorPrivacyController.removeCallback(mSensorPrivacyCallback);
-        super.onPause();
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
index bb7f721..468a75d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -52,12 +52,12 @@
     interface Callback {
 
         /** Notifies that a display has been added. */
-        @JvmDefault fun onDisplayAdded(displayId: Int) {}
+        fun onDisplayAdded(displayId: Int) {}
 
         /** Notifies that a display has been removed. */
-        @JvmDefault fun onDisplayRemoved(displayId: Int) {}
+        fun onDisplayRemoved(displayId: Int) {}
 
         /** Notifies a display has been changed */
-        @JvmDefault fun onDisplayChanged(displayId: Int) {}
+        fun onDisplayChanged(displayId: Int) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 33a3125..93a3e90 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -71,7 +71,6 @@
          * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
          * auto-decremented after the completion of this method.
          */
-        @JvmDefault
         fun onUserChanging(newUser: Int, userContext: Context) {}
 
         /**
@@ -82,7 +81,6 @@
          * user switch duration. When overriding this method, countDown() MUST be called on the
          * latch once execution is complete.
          */
-        @JvmDefault
         fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
             onUserChanging(newUser, userContext)
             latch.countDown()
@@ -93,13 +91,11 @@
          * Override this method to run things after the screen is unfrozen for the user switch.
          * Please see {@link #onUserChanging} if you need to hide jank.
          */
-        @JvmDefault
         fun onUserChanged(newUser: Int, userContext: Context) {}
 
         /**
          * Notifies that the current user's profiles have changed.
          */
-        @JvmDefault
         fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 9399d48..202d6e6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1169,6 +1169,9 @@
     private void updateViewControllers(KeyguardStatusView keyguardStatusView,
             FrameLayout userAvatarView,
             KeyguardUserSwitcherView keyguardUserSwitcherView) {
+        if (mKeyguardStatusViewController != null) {
+            mKeyguardStatusViewController.onDestroy();
+        }
         // Re-associate the KeyguardStatusViewController
         KeyguardStatusViewComponent statusViewComponent =
                 mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index bb2be66..b328c55 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -543,7 +543,6 @@
                 state.forceUserActivity,
                 state.launchingActivityFromNotification,
                 state.mediaBackdropShowing,
-                state.wallpaperSupportsAmbientMode,
                 state.windowNotTouchable,
                 state.componentsForcingTopUi,
                 state.forceOpenTokens,
@@ -734,12 +733,6 @@
         apply(mCurrentState);
     }
 
-    @Override
-    public void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {
-        mCurrentState.wallpaperSupportsAmbientMode = supportsAmbientMode;
-        apply(mCurrentState);
-    }
-
     /**
      * @param state The {@link StatusBarStateController} of the status bar.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
index 7812f07..d252943 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowState.kt
@@ -46,7 +46,6 @@
     @JvmField var forceUserActivity: Boolean = false,
     @JvmField var launchingActivityFromNotification: Boolean = false,
     @JvmField var mediaBackdropShowing: Boolean = false,
-    @JvmField var wallpaperSupportsAmbientMode: Boolean = false,
     @JvmField var windowNotTouchable: Boolean = false,
     @JvmField var componentsForcingTopUi: MutableSet<String> = mutableSetOf(),
     @JvmField var forceOpenTokens: MutableSet<Any> = mutableSetOf(),
@@ -84,7 +83,6 @@
             forceUserActivity.toString(),
             launchingActivityFromNotification.toString(),
             mediaBackdropShowing.toString(),
-            wallpaperSupportsAmbientMode.toString(),
             windowNotTouchable.toString(),
             componentsForcingTopUi.toString(),
             forceOpenTokens.toString(),
@@ -124,7 +122,6 @@
             forceUserActivity: Boolean,
             launchingActivity: Boolean,
             backdropShowing: Boolean,
-            wallpaperSupportsAmbientMode: Boolean,
             notTouchable: Boolean,
             componentsForcingTopUi: MutableSet<String>,
             forceOpenTokens: MutableSet<Any>,
@@ -153,7 +150,6 @@
                 this.forceUserActivity = forceUserActivity
                 this.launchingActivityFromNotification = launchingActivity
                 this.mediaBackdropShowing = backdropShowing
-                this.wallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode
                 this.windowNotTouchable = notTouchable
                 this.componentsForcingTopUi.clear()
                 this.componentsForcingTopUi.addAll(componentsForcingTopUi)
@@ -200,7 +196,6 @@
                 "forceUserActivity",
                 "launchingActivity",
                 "backdropShowing",
-                "wallpaperSupportsAmbientMode",
                 "notTouchable",
                 "componentsForcingTopUi",
                 "forceOpenTokens",
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
new file mode 100644
index 0000000..7a803867
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeEmptyImplModule.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.shade
+
+import com.android.systemui.dagger.SysUISingleton
+import dagger.Binds
+import dagger.Module
+
+/** Fulfills dependencies on the shade with empty implementations for variants with no shade. */
+@Module
+abstract class ShadeEmptyImplModule {
+    @Binds
+    @SysUISingleton
+    abstract fun bindsShadeViewController(svc: ShadeViewControllerEmptyImpl): ShadeViewController
+
+    @Binds
+    @SysUISingleton
+    abstract fun bindsShadeController(sc: ShadeControllerEmptyImpl): ShadeController
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 8b89ff4..529f12e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -54,7 +54,7 @@
 import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_TRANSITION_ID
 import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
 import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
-import com.android.systemui.shade.ShadeModule.Companion.SHADE_HEADER
+import com.android.systemui.shade.ShadeViewProviderModule.Companion.SHADE_HEADER
 import com.android.systemui.shade.carrier.ShadeCarrierGroup
 import com.android.systemui.shade.carrier.ShadeCarrierGroupController
 import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index be21df1..89aaaaf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -16,293 +16,21 @@
 
 package com.android.systemui.shade
 
-import android.annotation.SuppressLint
-import android.content.ContentResolver
-import android.os.Handler
-import android.view.LayoutInflater
-import android.view.ViewStub
-import androidx.constraintlayout.motion.widget.MotionLayout
-import com.android.keyguard.LockIconView
-import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.battery.BatteryMeterView
-import com.android.systemui.battery.BatteryMeterViewController
-import com.android.systemui.biometrics.AuthRippleController
-import com.android.systemui.biometrics.AuthRippleView
-import com.android.systemui.compose.ComposeFacade
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.privacy.OngoingPrivacyChip
-import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShelf
-import com.android.systemui.statusbar.NotificationShelfController
-import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
-import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
-import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
-import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.phone.StatusIconContainer
-import com.android.systemui.statusbar.phone.TapAgainView
-import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.tuner.TunerService
+
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-import javax.inject.Named
-import javax.inject.Provider
 
 /** Module for classes related to the notification shade. */
-@Module(includes = [StartShadeModule::class])
+@Module(includes = [StartShadeModule::class, ShadeViewProviderModule::class])
 abstract class ShadeModule {
-
-    @Binds
-    @IntoMap
-    @ClassKey(AuthRippleController::class)
-    abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
-
     @Binds
     @SysUISingleton
     abstract fun bindsShadeViewController(
         notificationPanelViewController: NotificationPanelViewController
     ): ShadeViewController
 
-    companion object {
-        const val SHADE_HEADER = "large_screen_shade_header"
-
-        @SuppressLint("InflateParams") // Root views don't have parents.
-        @Provides
-        @SysUISingleton
-        fun providesWindowRootView(
-            layoutInflater: LayoutInflater,
-            featureFlags: FeatureFlags,
-        ): WindowRootView {
-            return if (
-                featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
-            ) {
-                layoutInflater.inflate(R.layout.scene_window_root, null)
-            } else {
-                layoutInflater.inflate(R.layout.super_notification_shade, null)
-            }
-                as WindowRootView?
-                ?: throw IllegalStateException("Window root view could not be properly inflated")
-        }
-
-        @Provides
-        @SysUISingleton
-        // TODO(b/277762009): Do something similar to
-        //  {@link StatusBarWindowModule.InternalWindowView} so that only
-        //  {@link NotificationShadeWindowViewController} can inject this view.
-        fun providesNotificationShadeWindowView(
-            root: WindowRootView,
-            featureFlags: FeatureFlags,
-        ): NotificationShadeWindowView {
-            if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
-                return root.findViewById(R.id.legacy_window_root)
-            }
-            return root as NotificationShadeWindowView?
-                ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        fun providesNotificationStackScrollLayout(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): NotificationStackScrollLayout {
-            return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
-        }
-
-        @Provides
-        @SysUISingleton
-        fun providesNotificationShelfController(
-            featureFlags: FeatureFlags,
-            newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
-            notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
-            layoutInflater: LayoutInflater,
-            notificationStackScrollLayout: NotificationStackScrollLayout,
-        ): NotificationShelfController {
-            return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
-                newImpl.get()
-            } else {
-                val shelfView =
-                    layoutInflater.inflate(
-                        R.layout.status_bar_notification_shelf,
-                        notificationStackScrollLayout,
-                        false
-                    ) as NotificationShelf
-                val component =
-                    notificationShelfComponentBuilder.notificationShelf(shelfView).build()
-                val notificationShelfController = component.notificationShelfController
-                notificationShelfController.init()
-                notificationShelfController
-            }
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        fun providesNotificationPanelView(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): NotificationPanelView {
-            return notificationShadeWindowView.findViewById(R.id.notification_panel)
-        }
-
-        /**
-         * Constructs a new, unattached [KeyguardBottomAreaView].
-         *
-         * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
-         */
-        @Provides
-        fun providesKeyguardBottomAreaView(
-            npv: NotificationPanelView,
-            layoutInflater: LayoutInflater,
-        ): KeyguardBottomAreaView {
-            return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
-                as KeyguardBottomAreaView
-        }
-
-        @Provides
-        @SysUISingleton
-        fun providesLightRevealScrim(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): LightRevealScrim {
-            return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
-        }
-
-        @Provides
-        @SysUISingleton
-        fun providesKeyguardRootView(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): KeyguardRootView {
-            return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
-        }
-
-        @Provides
-        @SysUISingleton
-        fun providesSharedNotificationContainer(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): SharedNotificationContainer {
-            return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        fun providesAuthRippleView(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): AuthRippleView? {
-            return notificationShadeWindowView.findViewById(R.id.auth_ripple)
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        fun providesLockIconView(
-            keyguardRootView: KeyguardRootView,
-            notificationPanelView: NotificationPanelView,
-            featureFlags: FeatureFlags
-        ): LockIconView {
-            if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
-                return keyguardRootView.findViewById(R.id.lock_icon_view)
-            } else {
-                return notificationPanelView.findViewById(R.id.lock_icon_view)
-            }
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        fun providesTapAgainView(
-            notificationPanelView: NotificationPanelView,
-        ): TapAgainView {
-            return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        fun providesNotificationsQuickSettingsContainer(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): NotificationsQuickSettingsContainer {
-            return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        @Named(SHADE_HEADER)
-        fun providesShadeHeaderView(
-            notificationShadeWindowView: NotificationShadeWindowView,
-        ): MotionLayout {
-            val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
-            val layoutId = R.layout.combined_qs_header
-            stub.layoutResource = layoutId
-            return stub.inflate() as MotionLayout
-        }
-
-        @Provides
-        @SysUISingleton
-        fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
-            return CombinedShadeHeadersConstraintManagerImpl
-        }
-
-        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
-        @Provides
-        @SysUISingleton
-        @Named(SHADE_HEADER)
-        fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
-            return view.findViewById(R.id.batteryRemainingIcon)
-        }
-
-        @Provides
-        @SysUISingleton
-        @Named(SHADE_HEADER)
-        fun providesBatteryMeterViewController(
-            @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
-            userTracker: UserTracker,
-            configurationController: ConfigurationController,
-            tunerService: TunerService,
-            @Main mainHandler: Handler,
-            contentResolver: ContentResolver,
-            batteryController: BatteryController,
-        ): BatteryMeterViewController {
-            return BatteryMeterViewController(
-                batteryMeterView,
-                StatusBarLocation.QS,
-                userTracker,
-                configurationController,
-                tunerService,
-                mainHandler,
-                contentResolver,
-                batteryController,
-            )
-        }
-
-        @Provides
-        @SysUISingleton
-        @Named(SHADE_HEADER)
-        fun providesOngoingPrivacyChip(
-            @Named(SHADE_HEADER) header: MotionLayout,
-        ): OngoingPrivacyChip {
-            return header.findViewById(R.id.privacy_chip)
-        }
-
-        @Provides
-        @SysUISingleton
-        @Named(SHADE_HEADER)
-        fun providesStatusIconContainer(
-            @Named(SHADE_HEADER) header: MotionLayout,
-        ): StatusIconContainer {
-            return header.findViewById(R.id.statusIcons)
-        }
-    }
+    @Binds
+    @SysUISingleton
+    abstract fun bindsShadeController(shadeControllerImpl: ShadeControllerImpl): ShadeController
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index 56bb1a6..5804040 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -29,12 +29,12 @@
     interface ShadeStateEventsListener {
 
         /** Invoked when the notification panel starts or stops collapsing. */
-        @JvmDefault fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
+        fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
 
         /**
          * Invoked when the notification panel starts or stops launching an [android.app.Activity].
          */
-        @JvmDefault fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
+        fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
 
         /**
          * Invoked when the "expand immediate" attribute changes.
@@ -45,6 +45,6 @@
          * Another example is when full QS is showing, and we swipe up from the bottom. Instead of
          * going to QQS, the panel fully collapses.
          */
-        @JvmDefault fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
+        fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
new file mode 100644
index 0000000..fc6479e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -0,0 +1,309 @@
+/*
+ * 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.shade
+
+import android.annotation.SuppressLint
+import android.content.ContentResolver
+import android.os.Handler
+import android.view.LayoutInflater
+import android.view.ViewStub
+import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.keyguard.LockIconView
+import com.android.systemui.R
+import com.android.systemui.battery.BatteryMeterView
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.biometrics.AuthRippleView
+import com.android.systemui.compose.ComposeFacade
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.privacy.OngoingPrivacyChip
+import com.android.systemui.scene.shared.model.Scene
+import com.android.systemui.scene.shared.model.SceneContainerConfig
+import com.android.systemui.scene.shared.model.SceneContainerNames
+import com.android.systemui.scene.ui.view.SceneWindowRootView
+import com.android.systemui.scene.ui.view.WindowRootView
+import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
+import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.NotificationShelf
+import com.android.systemui.statusbar.NotificationShelfController
+import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
+import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
+import com.android.systemui.statusbar.phone.StatusBarLocation
+import com.android.systemui.statusbar.phone.StatusIconContainer
+import com.android.systemui.statusbar.phone.TapAgainView
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.tuner.TunerService
+import dagger.Module
+import dagger.Provides
+import javax.inject.Named
+import javax.inject.Provider
+
+/** Module for providing views related to the shade. */
+@Module
+abstract class ShadeViewProviderModule {
+    companion object {
+        const val SHADE_HEADER = "large_screen_shade_header"
+
+        @SuppressLint("InflateParams") // Root views don't have parents.
+        @Provides
+        @SysUISingleton
+        fun providesWindowRootView(
+            layoutInflater: LayoutInflater,
+            featureFlags: FeatureFlags,
+            @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+            viewModelProvider: Provider<SceneContainerViewModel>,
+            @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+            containerConfigProvider: Provider<SceneContainerConfig>,
+            @Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
+            scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
+        ): WindowRootView {
+            return if (
+                featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
+            ) {
+                val sceneWindowRootView =
+                    layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
+                sceneWindowRootView.init(
+                    viewModel = viewModelProvider.get(),
+                    containerConfig = containerConfigProvider.get(),
+                    scenes = scenesProvider.get(),
+                )
+                sceneWindowRootView
+            } else {
+                layoutInflater.inflate(R.layout.super_notification_shade, null)
+            }
+                as WindowRootView?
+                ?: throw IllegalStateException("Window root view could not be properly inflated")
+        }
+
+        @Provides
+        @SysUISingleton
+        // TODO(b/277762009): Do something similar to
+        //  {@link StatusBarWindowModule.InternalWindowView} so that only
+        //  {@link NotificationShadeWindowViewController} can inject this view.
+        fun providesNotificationShadeWindowView(
+            root: WindowRootView,
+            featureFlags: FeatureFlags,
+        ): NotificationShadeWindowView {
+            if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
+                return root.findViewById(R.id.legacy_window_root)
+            }
+            return root as NotificationShadeWindowView?
+                ?: throw IllegalStateException("root view not a NotificationShadeWindowView")
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        fun providesNotificationStackScrollLayout(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): NotificationStackScrollLayout {
+            return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
+        }
+
+        @Provides
+        @SysUISingleton
+        fun providesNotificationShelfController(
+            featureFlags: FeatureFlags,
+            newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
+            notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
+            layoutInflater: LayoutInflater,
+            notificationStackScrollLayout: NotificationStackScrollLayout,
+        ): NotificationShelfController {
+            return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+                newImpl.get()
+            } else {
+                val shelfView =
+                    layoutInflater.inflate(
+                        R.layout.status_bar_notification_shelf,
+                        notificationStackScrollLayout,
+                        false
+                    ) as NotificationShelf
+                val component =
+                    notificationShelfComponentBuilder.notificationShelf(shelfView).build()
+                val notificationShelfController = component.notificationShelfController
+                notificationShelfController.init()
+                notificationShelfController
+            }
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        fun providesNotificationPanelView(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): NotificationPanelView {
+            return notificationShadeWindowView.findViewById(R.id.notification_panel)
+        }
+
+        /**
+         * Constructs a new, unattached [KeyguardBottomAreaView].
+         *
+         * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+         */
+        @Provides
+        fun providesKeyguardBottomAreaView(
+            npv: NotificationPanelView,
+            layoutInflater: LayoutInflater,
+        ): KeyguardBottomAreaView {
+            return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
+                as KeyguardBottomAreaView
+        }
+
+        @Provides
+        @SysUISingleton
+        fun providesLightRevealScrim(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): LightRevealScrim {
+            return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
+        }
+
+        @Provides
+        @SysUISingleton
+        fun providesKeyguardRootView(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): KeyguardRootView {
+            return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
+        }
+
+        @Provides
+        @SysUISingleton
+        fun providesSharedNotificationContainer(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): SharedNotificationContainer {
+            return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        fun providesAuthRippleView(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): AuthRippleView? {
+            return notificationShadeWindowView.findViewById(R.id.auth_ripple)
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        fun providesLockIconView(
+            keyguardRootView: KeyguardRootView,
+            notificationPanelView: NotificationPanelView,
+            featureFlags: FeatureFlags
+        ): LockIconView {
+            if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+                return keyguardRootView.findViewById(R.id.lock_icon_view)
+            } else {
+                return notificationPanelView.findViewById(R.id.lock_icon_view)
+            }
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        fun providesTapAgainView(
+            notificationPanelView: NotificationPanelView,
+        ): TapAgainView {
+            return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        fun providesNotificationsQuickSettingsContainer(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): NotificationsQuickSettingsContainer {
+            return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        @Named(SHADE_HEADER)
+        fun providesShadeHeaderView(
+            notificationShadeWindowView: NotificationShadeWindowView,
+        ): MotionLayout {
+            val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
+            val layoutId = R.layout.combined_qs_header
+            stub.layoutResource = layoutId
+            return stub.inflate() as MotionLayout
+        }
+
+        @Provides
+        @SysUISingleton
+        fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
+            return CombinedShadeHeadersConstraintManagerImpl
+        }
+
+        // TODO(b/277762009): Only allow this view's controller to inject the view. See above.
+        @Provides
+        @SysUISingleton
+        @Named(SHADE_HEADER)
+        fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
+            return view.findViewById(R.id.batteryRemainingIcon)
+        }
+
+        @Provides
+        @SysUISingleton
+        @Named(SHADE_HEADER)
+        fun providesBatteryMeterViewController(
+            @Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
+            userTracker: UserTracker,
+            configurationController: ConfigurationController,
+            tunerService: TunerService,
+            @Main mainHandler: Handler,
+            contentResolver: ContentResolver,
+            batteryController: BatteryController,
+        ): BatteryMeterViewController {
+            return BatteryMeterViewController(
+                batteryMeterView,
+                StatusBarLocation.QS,
+                userTracker,
+                configurationController,
+                tunerService,
+                mainHandler,
+                contentResolver,
+                batteryController,
+            )
+        }
+
+        @Provides
+        @SysUISingleton
+        @Named(SHADE_HEADER)
+        fun providesOngoingPrivacyChip(
+            @Named(SHADE_HEADER) header: MotionLayout,
+        ): OngoingPrivacyChip {
+            return header.findViewById(R.id.privacy_chip)
+        }
+
+        @Provides
+        @SysUISingleton
+        @Named(SHADE_HEADER)
+        fun providesStatusIconContainer(
+            @Named(SHADE_HEADER) header: MotionLayout,
+        ): StatusIconContainer {
+            return header.findViewById(R.id.statusIcons)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
index c50693c..15ec18c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.biometrics.AuthRippleController
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
@@ -28,4 +29,9 @@
     @IntoMap
     @ClassKey(ShadeController::class)
     abstract fun bind(shadeController: ShadeController): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(AuthRippleController::class)
+    abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt
index 5d06f8d0..15ff31f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt
@@ -21,11 +21,12 @@
 
 /**
  * A temporary base class that's shared between our old status bar connectivity view implementations
- * ([StatusBarMobileView]) and our new status bar implementations ([ModernStatusBarWifiView],
- * [ModernStatusBarMobileView]).
+ * and our new status bar implementations ([ModernStatusBarWifiView], [ModernStatusBarMobileView]).
  *
  * Once our refactor is over, we should be able to delete this go-between class and the old view
  * class.
+ *
+ * NOTE: the old classes are now deleted, and this class can be removed.
  */
 abstract class BaseStatusBarFrameLayout
 @JvmOverloads
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index 4ec5f46..7a989cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
@@ -19,7 +19,6 @@
 import android.view.View;
 
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -52,7 +51,6 @@
         mActivatableNotificationViewController = activatableNotificationViewController;
         mKeyguardBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
-        mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
         mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fb88a96..763400b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -27,10 +27,12 @@
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Point;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
 import android.media.MediaMetadata;
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
@@ -41,6 +43,7 @@
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.Log;
+import android.view.Display;
 import android.view.View;
 import android.widget.ImageView;
 
@@ -74,11 +77,15 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Comparator;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import dagger.Lazy;
 
@@ -138,6 +145,14 @@
     private BackDropView mBackdrop;
     private ImageView mBackdropFront;
     private ImageView mBackdropBack;
+    private final Point mTmpDisplaySize = new Point();
+
+    private final DisplayManager mDisplayManager;
+    @Nullable
+    private List<String> mSmallerInternalDisplayUids;
+    private Display mCurrentDisplay;
+
+    private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
 
     private final MediaController.Callback mMediaListener = new MediaController.Callback() {
         @Override
@@ -184,7 +199,8 @@
             SysuiColorExtractor colorExtractor,
             KeyguardStateController keyguardStateController,
             DumpManager dumpManager,
-            WallpaperManager wallpaperManager) {
+            WallpaperManager wallpaperManager,
+            DisplayManager displayManager) {
         mContext = context;
         mMediaArtworkProcessor = mediaArtworkProcessor;
         mKeyguardBypassController = keyguardBypassController;
@@ -200,6 +216,7 @@
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
         mKeyguardStateController = keyguardStateController;
+        mDisplayManager = displayManager;
         mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled();
 
         setupNotifPipeline();
@@ -477,6 +494,48 @@
     }
 
     /**
+     * Notify lockscreen wallpaper drawable the current internal display.
+     */
+    public void onDisplayUpdated(Display display) {
+        Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
+        mCurrentDisplay = display;
+        if (mWallapperDrawable != null) {
+            mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
+        }
+        Trace.endSection();
+    }
+
+    private boolean isOnSmallerInternalDisplays() {
+        if (mSmallerInternalDisplayUids == null) {
+            mSmallerInternalDisplayUids = findSmallerInternalDisplayUids();
+        }
+        return mSmallerInternalDisplayUids.contains(mCurrentDisplay.getUniqueId());
+    }
+
+    private List<String> findSmallerInternalDisplayUids() {
+        if (mSmallerInternalDisplayUids != null) {
+            return mSmallerInternalDisplayUids;
+        }
+        List<Display> internalDisplays = Arrays.stream(mDisplayManager.getDisplays(
+                        DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+                .filter(display -> display.getType() == Display.TYPE_INTERNAL)
+                .collect(Collectors.toList());
+        if (internalDisplays.isEmpty()) {
+            return List.of();
+        }
+        Display largestDisplay = internalDisplays.stream()
+                .max(Comparator.comparingInt(this::getRealDisplayArea))
+                .orElse(internalDisplays.get(0));
+        internalDisplays.remove(largestDisplay);
+        return internalDisplays.stream().map(Display::getUniqueId).collect(Collectors.toList());
+    }
+
+    private int getRealDisplayArea(Display display) {
+        display.getRealSize(mTmpDisplaySize);
+        return mTmpDisplaySize.x * mTmpDisplaySize.y;
+    }
+
+    /**
      * Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
      */
     public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
@@ -551,7 +610,7 @@
                     mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
             if (lockWallpaper != null) {
                 artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
-                        mBackdropBack.getResources(), lockWallpaper);
+                        mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
                 // We're in the SHADE mode on the SIM screen - yet we still need to show
                 // the lockscreen wallpaper in that mode.
                 allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
@@ -611,6 +670,10 @@
                     mBackdropBack.setBackgroundColor(0xFFFFFFFF);
                     mBackdropBack.setImageDrawable(new ColorDrawable(c));
                 } else {
+                    if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
+                        mWallapperDrawable =
+                                (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
+                    }
                     mBackdropBack.setImageDrawable(artworkDrawable);
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0e20df6..b624115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -553,7 +553,6 @@
          */
         fun onWallpaperZoomOutChanged(zoomOut: Float)
 
-        @JvmDefault
         fun onBlurRadiusChanged(blurRadius: Int) {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 47a4641..5ac542b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -112,9 +112,6 @@
     /** Sets the state of whether heads up is showing or not. */
     default void setHeadsUpShowing(boolean showing) {}
 
-    /** Sets whether the wallpaper supports ambient mode or not. */
-    default void setWallpaperSupportsAmbientMode(boolean supportsAmbientMode) {}
-
     /** Gets whether the wallpaper is showing or not. */
     default boolean isShowingWallpaper() {
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 25a1dc6..3f37c60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -24,7 +24,6 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.IndentingPrintWriter;
-import android.util.Log;
 import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -40,6 +39,8 @@
 import com.android.internal.policy.SystemBarUtils;
 import com.android.systemui.R;
 import com.android.systemui.animation.ShadeInterpolation;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -95,8 +96,10 @@
     private float mCornerAnimationDistance;
     private NotificationShelfController mController;
     private float mActualWidth = -1;
-    private boolean mSensitiveRevealAnimEnabled;
-    private boolean mShelfRefactorFlagEnabled;
+    private final ViewRefactorFlag mSensitiveRevealAnim =
+            new ViewRefactorFlag(Flags.SENSITIVE_REVEAL_ANIM);
+    private final ViewRefactorFlag mShelfRefactor =
+            new ViewRefactorFlag(Flags.NOTIFICATION_SHELF_REFACTOR);
     private boolean mCanModifyColorOfNotifications;
     private boolean mCanInteract;
     private NotificationStackScrollLayout mHostLayout;
@@ -130,7 +133,7 @@
 
     public void bind(AmbientState ambientState,
                      NotificationStackScrollLayoutController hostLayoutController) {
-        assertRefactorFlagDisabled();
+        mShelfRefactor.assertDisabled();
         mAmbientState = ambientState;
         mHostLayoutController = hostLayoutController;
         hostLayoutController.setOnNotificationRemovedListener((child, isTransferInProgress) -> {
@@ -140,7 +143,7 @@
 
     public void bind(AmbientState ambientState, NotificationStackScrollLayout hostLayout,
             NotificationRoundnessManager roundnessManager) {
-        if (!checkRefactorFlagEnabled()) return;
+        if (!mShelfRefactor.expectEnabled()) return;
         mAmbientState = ambientState;
         mHostLayout = hostLayout;
         mRoundnessManager = roundnessManager;
@@ -268,7 +271,7 @@
         }
 
         final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight();
-        if (mSensitiveRevealAnimEnabled && viewState.hidden) {
+        if (mSensitiveRevealAnim.isEnabled() && viewState.hidden) {
             // if the shelf is hidden, position it at the end of the stack (plus the clip
             // padding), such that when it appears animated, it will smoothly move in from the
             // bottom, without jump cutting any notifications
@@ -279,7 +282,7 @@
     }
 
     private int getSpeedBumpIndex() {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mHostLayout.getSpeedBumpIndex();
         } else {
             return mHostLayoutController.getSpeedBumpIndex();
@@ -413,7 +416,7 @@
                     expandingAnimated, isLastChild, shelfClipStart);
 
             // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount
-            if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf())
+            if ((!mSensitiveRevealAnim.isEnabled() && ((isLastChild && !child.isInShelf())
                     || backgroundForceHidden)) || aboveShelf) {
                 notificationClipEnd = shelfStart + getIntrinsicHeight();
             } else {
@@ -462,7 +465,7 @@
                 // if the shelf is visible, but if the shelf is hidden, it causes incorrect curling.
                 // notificationClipEnd handles the discrepancy between a visible and hidden shelf,
                 // so we use that when on the keyguard (and while animating away) to reduce curling.
-                final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled
+                final float keyguardSafeShelfStart = !mSensitiveRevealAnim.isEnabled()
                         && mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart;
                 updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart);
             }
@@ -504,7 +507,7 @@
     }
 
     private ExpandableView getHostLayoutChildAt(int index) {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return (ExpandableView) mHostLayout.getChildAt(index);
         } else {
             return mHostLayoutController.getChildAt(index);
@@ -512,7 +515,7 @@
     }
 
     private int getHostLayoutChildCount() {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mHostLayout.getChildCount();
         } else {
             return mHostLayoutController.getChildCount();
@@ -520,7 +523,7 @@
     }
 
     private boolean canModifyColorOfNotifications() {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mCanModifyColorOfNotifications && mAmbientState.isShadeExpanded();
         } else {
             return mController.canModifyColorOfNotifications();
@@ -583,7 +586,7 @@
     }
 
     private boolean isViewAffectedBySwipe(ExpandableView expandableView) {
-        if (!mShelfRefactorFlagEnabled) {
+        if (!mShelfRefactor.isEnabled()) {
             return mHostLayoutController.isViewAffectedBySwipe(expandableView);
         } else {
             return mRoundnessManager.isViewAffectedBySwipe(expandableView);
@@ -607,7 +610,7 @@
     }
 
     private View getHostLayoutTransientView(int index) {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mHostLayout.getTransientView(index);
         } else {
             return mHostLayoutController.getTransientView(index);
@@ -615,7 +618,7 @@
     }
 
     private int getHostLayoutTransientViewCount() {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mHostLayout.getTransientViewCount();
         } else {
             return mHostLayoutController.getTransientViewCount();
@@ -961,7 +964,7 @@
 
     @Override
     public void onStateChanged(int newState) {
-        assertRefactorFlagDisabled();
+        mShelfRefactor.assertDisabled();
         mStatusBarState = newState;
         updateInteractiveness();
     }
@@ -975,7 +978,7 @@
     }
 
     private boolean canInteract() {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mCanInteract;
         } else {
             return mStatusBarState == StatusBarState.KEYGUARD;
@@ -1018,32 +1021,18 @@
         return false;
     }
 
-    private void assertRefactorFlagDisabled() {
-        if (mShelfRefactorFlagEnabled) {
-            NotificationShelfController.throwIllegalFlagStateError(false);
-        }
-    }
-
-    private boolean checkRefactorFlagEnabled() {
-        if (!mShelfRefactorFlagEnabled) {
-            Log.wtf(TAG,
-                    "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is disabled.");
-        }
-        return mShelfRefactorFlagEnabled;
-    }
-
     public void setController(NotificationShelfController notificationShelfController) {
-        assertRefactorFlagDisabled();
+        mShelfRefactor.assertDisabled();
         mController = notificationShelfController;
     }
 
     public void setCanModifyColorOfNotifications(boolean canModifyColorOfNotifications) {
-        if (!checkRefactorFlagEnabled()) return;
+        if (!mShelfRefactor.expectEnabled()) return;
         mCanModifyColorOfNotifications = canModifyColorOfNotifications;
     }
 
     public void setCanInteract(boolean canInteract) {
-        if (!checkRefactorFlagEnabled()) return;
+        if (!mShelfRefactor.expectEnabled()) return;
         mCanInteract = canInteract;
         updateInteractiveness();
     }
@@ -1053,27 +1042,15 @@
     }
 
     private int getIndexOfViewInHostLayout(ExpandableView child) {
-        if (mShelfRefactorFlagEnabled) {
+        if (mShelfRefactor.isEnabled()) {
             return mHostLayout.indexOfChild(child);
         } else {
             return mHostLayoutController.indexOfChild(child);
         }
     }
 
-    /**
-     * Set whether the sensitive reveal animation feature flag is enabled
-     * @param enabled true if enabled
-     */
-    public void setSensitiveRevealAnimEnabled(boolean enabled) {
-        mSensitiveRevealAnimEnabled = enabled;
-    }
-
-    public void setRefactorFlagEnabled(boolean enabled) {
-        mShelfRefactorFlagEnabled = enabled;
-    }
-
     public void requestRoundnessResetFor(ExpandableView child) {
-        if (!checkRefactorFlagEnabled()) return;
+        if (!mShelfRefactor.expectEnabled()) return;
         child.requestRoundnessReset(SHELF_SCROLL);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
index 1619dda..8a3e217 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.kt
@@ -16,11 +16,8 @@
 
 package com.android.systemui.statusbar
 
-import android.util.Log
 import android.view.View
 import android.view.View.OnClickListener
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.stack.AmbientState
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -49,29 +46,4 @@
 
     /** @see View.setOnClickListener */
     fun setOnClickListener(listener: OnClickListener)
-
-    companion object {
-        @JvmStatic
-        fun assertRefactorFlagDisabled(featureFlags: FeatureFlags) {
-            if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
-                throwIllegalFlagStateError(expected = false)
-            }
-        }
-
-        @JvmStatic
-        fun checkRefactorFlagEnabled(featureFlags: FeatureFlags): Boolean =
-            featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR).also { enabled ->
-                if (!enabled) {
-                    Log.wtf("NotifShelf", getErrorMessage(expected = true))
-                }
-            }
-
-        @JvmStatic
-        fun throwIllegalFlagStateError(expected: Boolean): Nothing =
-            error(getErrorMessage(expected))
-
-        private fun getErrorMessage(expected: Boolean): String =
-            "Code path not supported when Flags.NOTIFICATION_SHELF_REFACTOR is " +
-                if (expected) "disabled" else "enabled"
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
deleted file mode 100644
index d6f6c2c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import static com.android.systemui.plugins.DarkIconDispatcher.getTint;
-import static com.android.systemui.plugins.DarkIconDispatcher.isInAreas;
-import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT;
-import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN;
-import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.graph.SignalDrawable;
-import com.android.systemui.DualToneHandler;
-import com.android.systemui.R;
-import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-
-import java.util.ArrayList;
-
-/**
- * View group for the mobile icon in the status bar
- */
-public class StatusBarMobileView extends BaseStatusBarFrameLayout implements DarkReceiver,
-        StatusIconDisplayable {
-    private static final String TAG = "StatusBarMobileView";
-
-    /// Used to show etc dots
-    private StatusBarIconView mDotView;
-    /// The main icon view
-    private LinearLayout mMobileGroup;
-    private String mSlot;
-    private MobileIconState mState;
-    private SignalDrawable mMobileDrawable;
-    private View mInoutContainer;
-    private ImageView mIn;
-    private ImageView mOut;
-    private ImageView mMobile, mMobileType, mMobileRoaming;
-    private View mMobileRoamingSpace;
-    @StatusBarIconView.VisibleState
-    private int mVisibleState = STATE_HIDDEN;
-    private DualToneHandler mDualToneHandler;
-    private boolean mForceHidden;
-
-    /**
-     * Designated constructor
-     *
-     * This view is special, in that it is the only view in SystemUI that allows for a configuration
-     * override on a MCC/MNC-basis. This means that for every mobile view inflated, we have to
-     * construct a context with that override, since the resource system doesn't have a way to
-     * handle this for us.
-     *
-     * @param context A context with resources configured by MCC/MNC
-     * @param slot The string key defining which slot this icon refers to. Always "mobile" for the
-     *             mobile icon
-     */
-    public static StatusBarMobileView fromContext(
-            Context context,
-            String slot
-    ) {
-        LayoutInflater inflater = LayoutInflater.from(context);
-        StatusBarMobileView v = (StatusBarMobileView)
-                inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
-        v.setSlot(slot);
-        v.init();
-        v.setVisibleState(STATE_ICON);
-        return v;
-    }
-
-    public StatusBarMobileView(Context context) {
-        super(context);
-    }
-
-    public StatusBarMobileView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    public void getDrawingRect(Rect outRect) {
-        super.getDrawingRect(outRect);
-        float translationX = getTranslationX();
-        float translationY = getTranslationY();
-        outRect.left += translationX;
-        outRect.right += translationX;
-        outRect.top += translationY;
-        outRect.bottom += translationY;
-    }
-
-    private void init() {
-        mDualToneHandler = new DualToneHandler(getContext());
-        mMobileGroup = findViewById(R.id.mobile_group);
-        mMobile = findViewById(R.id.mobile_signal);
-        mMobileType = findViewById(R.id.mobile_type);
-        mMobileRoaming = findViewById(R.id.mobile_roaming);
-        mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
-        mIn = findViewById(R.id.mobile_in);
-        mOut = findViewById(R.id.mobile_out);
-        mInoutContainer = findViewById(R.id.inout_container);
-
-        mMobileDrawable = new SignalDrawable(getContext());
-        mMobile.setImageDrawable(mMobileDrawable);
-
-        initDotView();
-    }
-
-    private void initDotView() {
-        mDotView = new StatusBarIconView(mContext, mSlot, null);
-        mDotView.setVisibleState(STATE_DOT);
-
-        int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size_sp);
-        LayoutParams lp = new LayoutParams(width, width);
-        lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START;
-        addView(mDotView, lp);
-    }
-
-    public void applyMobileState(MobileIconState state) {
-        boolean requestLayout = false;
-        if (state == null) {
-            requestLayout = getVisibility() != View.GONE;
-            setVisibility(View.GONE);
-            mState = null;
-        } else if (mState == null) {
-            requestLayout = true;
-            mState = state.copy();
-            initViewState();
-        } else if (!mState.equals(state)) {
-            requestLayout = updateState(state.copy());
-        }
-
-        if (requestLayout) {
-            requestLayout();
-        }
-    }
-
-    private void initViewState() {
-        setContentDescription(mState.contentDescription);
-        if (!mState.visible || mForceHidden) {
-            mMobileGroup.setVisibility(View.GONE);
-        } else {
-            mMobileGroup.setVisibility(View.VISIBLE);
-        }
-        mMobileDrawable.setLevel(mState.strengthId);
-        if (mState.typeId > 0) {
-            mMobileType.setContentDescription(mState.typeContentDescription);
-            mMobileType.setImageResource(mState.typeId);
-            mMobileType.setVisibility(View.VISIBLE);
-        } else {
-            mMobileType.setVisibility(View.GONE);
-        }
-        mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE);
-        mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
-        mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
-        mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
-        mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
-        mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
-                ? View.VISIBLE : View.GONE);
-    }
-
-    private boolean updateState(MobileIconState state) {
-        boolean needsLayout = false;
-
-        setContentDescription(state.contentDescription);
-        int newVisibility = state.visible && !mForceHidden ? View.VISIBLE : View.GONE;
-        if (newVisibility != mMobileGroup.getVisibility() && STATE_ICON == mVisibleState) {
-            mMobileGroup.setVisibility(newVisibility);
-            needsLayout = true;
-        }
-        if (mState.strengthId != state.strengthId) {
-            mMobileDrawable.setLevel(state.strengthId);
-        }
-        if (mState.typeId != state.typeId) {
-            needsLayout |= state.typeId == 0 || mState.typeId == 0;
-            if (state.typeId != 0) {
-                mMobileType.setContentDescription(state.typeContentDescription);
-                mMobileType.setImageResource(state.typeId);
-                mMobileType.setVisibility(View.VISIBLE);
-            } else {
-                mMobileType.setVisibility(View.GONE);
-            }
-        }
-
-        mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE);
-        mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
-        mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
-        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
-        mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
-        mInoutContainer.setVisibility((state.activityIn || state.activityOut)
-                ? View.VISIBLE : View.GONE);
-
-        needsLayout |= state.roaming != mState.roaming
-                || state.activityIn != mState.activityIn
-                || state.activityOut != mState.activityOut
-                || state.showTriangle != mState.showTriangle;
-
-        mState = state;
-        return needsLayout;
-    }
-
-    @Override
-    public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) {
-        float intensity = isInAreas(areas, this) ? darkIntensity : 0;
-        mMobileDrawable.setTintList(
-                ColorStateList.valueOf(mDualToneHandler.getSingleColor(intensity)));
-        ColorStateList color = ColorStateList.valueOf(getTint(areas, this, tint));
-        mIn.setImageTintList(color);
-        mOut.setImageTintList(color);
-        mMobileType.setImageTintList(color);
-        mMobileRoaming.setImageTintList(color);
-        mDotView.setDecorColor(tint);
-        mDotView.setIconColor(tint, false);
-    }
-
-    @Override
-    public String getSlot() {
-        return mSlot;
-    }
-
-    public void setSlot(String slot) {
-        mSlot = slot;
-    }
-
-    @Override
-    public void setStaticDrawableColor(int color) {
-        ColorStateList list = ColorStateList.valueOf(color);
-        mMobileDrawable.setTintList(list);
-        mIn.setImageTintList(list);
-        mOut.setImageTintList(list);
-        mMobileType.setImageTintList(list);
-        mMobileRoaming.setImageTintList(list);
-        mDotView.setDecorColor(color);
-    }
-
-    @Override
-    public void setDecorColor(int color) {
-        mDotView.setDecorColor(color);
-    }
-
-    @Override
-    public boolean isIconVisible() {
-        return mState.visible && !mForceHidden;
-    }
-
-    @Override
-    public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) {
-        if (state == mVisibleState) {
-            return;
-        }
-
-        mVisibleState = state;
-        switch (state) {
-            case STATE_ICON:
-                mMobileGroup.setVisibility(View.VISIBLE);
-                mDotView.setVisibility(View.GONE);
-                break;
-            case STATE_DOT:
-                mMobileGroup.setVisibility(View.INVISIBLE);
-                mDotView.setVisibility(View.VISIBLE);
-                break;
-            case STATE_HIDDEN:
-            default:
-                mMobileGroup.setVisibility(View.INVISIBLE);
-                mDotView.setVisibility(View.INVISIBLE);
-                break;
-        }
-    }
-
-    /**
-     * Forces the state to be hidden (views will be GONE) and if necessary updates the layout.
-     *
-     * Makes sure that the {@link StatusBarIconController} cannot make it visible while this flag
-     * is enabled.
-     * @param forceHidden {@code true} if the icon should be GONE in its view regardless of its
-     *                                state.
-     *               {@code false} if the icon should show as determined by its controller.
-     */
-    public void forceHidden(boolean forceHidden) {
-        if (mForceHidden != forceHidden) {
-            mForceHidden = forceHidden;
-            updateState(mState);
-            requestLayout();
-        }
-    }
-
-    @Override
-    @StatusBarIconView.VisibleState
-    public int getVisibleState() {
-        return mVisibleState;
-    }
-
-    @VisibleForTesting
-    public MobileIconState getState() {
-        return mState;
-    }
-
-    @Override
-    public String toString() {
-        return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")";
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 9aa28c3..93b9ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -1282,7 +1282,7 @@
             }
         }
         String sims = args.getString("sims");
-        if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
+        if (sims != null) {
             int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8);
             List<SubscriptionInfo> subs = new ArrayList<>();
             if (num != mMobileSignalControllers.size()) {
@@ -1305,7 +1305,7 @@
             mCallbackHandler.setNoSims(mHasNoSubs, mSimDetected);
         }
         String mobile = args.getString("mobile");
-        if (mobile != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
+        if (mobile != null) {
             boolean show = mobile.equals("show");
             String datatype = args.getString("datatype");
             String slotString = args.getString("slot");
@@ -1390,7 +1390,7 @@
             controller.notifyListeners();
         }
         String carrierNetworkChange = args.getString("carriernetworkchange");
-        if (carrierNetworkChange != null && !mStatusBarPipelineFlags.useNewMobileIcons()) {
+        if (carrierNetworkChange != null) {
             boolean show = carrierNetworkChange.equals("show");
             for (int i = 0; i < mMobileSignalControllers.size(); i++) {
                 MobileSignalController controller = mMobileSignalControllers.valueAt(i);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
index 599beec..6be407a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -29,7 +29,6 @@
      *
      * @param wifiIndicators a box type containing enough information to properly draw a Wi-Fi icon
      */
-    @JvmDefault
     fun setWifiIndicators(wifiIndicators: WifiIndicators) {}
 
     /**
@@ -42,7 +41,6 @@
      * NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be
      * indexed based on its [subId][MobileDataIndicators.subId]
      */
-    @JvmDefault
     fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {}
 
     /**
@@ -51,7 +49,6 @@
      *
      * @param subs a [SubscriptionInfo] for each subscription that we know about
      */
-    @JvmDefault
     fun setSubs(subs: List<@JvmSuppressWildcards SubscriptionInfo>) {}
 
     /**
@@ -63,7 +60,6 @@
      * @param show whether or not to show a "no sim" view
      * @param simDetected whether any SIM is detected or not
      */
-    @JvmDefault
     fun setNoSims(show: Boolean, simDetected: Boolean) {}
 
     /**
@@ -72,7 +68,6 @@
      *
      * @param icon an [IconState] for the current ethernet status
      */
-    @JvmDefault
     fun setEthernetIndicators(icon: IconState) {}
 
     /**
@@ -80,7 +75,6 @@
      *
      * @param icon an [IconState] for the current airplane mode status
      */
-    @JvmDefault
     fun setIsAirplaneMode(icon: IconState) {}
 
     /**
@@ -88,7 +82,6 @@
      *
      * @param enabled the current mobile data feature ennabled state
      */
-    @JvmDefault
     fun setMobileDataEnabled(enabled: Boolean) {}
 
     /**
@@ -97,7 +90,6 @@
      * @param noValidatedNetwork whether there is any validated network.
      * @param noNetworksAvailable whether there is any WiFi networks available.
      */
-    @JvmDefault
     fun setConnectivityStatus(
         noDefaultNetwork: Boolean,
         noValidatedNetwork: Boolean,
@@ -109,7 +101,6 @@
      * @param statusIcon the icon for the call indicator
      * @param subId subscription ID for which to update the UI
      */
-    @JvmDefault
     fun setCallIndicator(statusIcon: IconState, subId: Int) {}
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index e5ba3ce..1c7a186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,6 +19,7 @@
 import android.app.IActivityManager;
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.hardware.display.DisplayManager;
 import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
 import android.util.Log;
@@ -146,7 +147,8 @@
             SysuiColorExtractor colorExtractor,
             KeyguardStateController keyguardStateController,
             DumpManager dumpManager,
-            WallpaperManager wallpaperManager) {
+            WallpaperManager wallpaperManager,
+            DisplayManager displayManager) {
         return new NotificationMediaManager(
                 context,
                 centralSurfacesOptionalLazy,
@@ -162,7 +164,8 @@
                 colorExtractor,
                 keyguardStateController,
                 dumpManager,
-                wallpaperManager);
+                wallpaperManager,
+                displayManager);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 2a18f1f..ef90890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -49,11 +49,10 @@
     fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null }
 
     // Best method name, change my mind
-    @JvmDefault
     fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? {
         return null
     }
-    @JvmDefault fun onHidePersistentDot(): Animator? { return null }
+    fun onHidePersistentDot(): Animator? { return null }
 }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
index 577ad20..bac8982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification
 
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.FlagResolver
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
 import javax.inject.Inject
@@ -25,12 +23,8 @@
 class NotifPipelineFlags
 @Inject
 constructor(
-    private val featureFlags: FeatureFlags,
-    private val sysPropFlags: FlagResolver,
+    private val featureFlags: FeatureFlags
 ) {
     fun isDevLoggingEnabled(): Boolean =
         featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING)
-
-    fun allowDismissOngoing(): Boolean =
-        sysPropFlags.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 9ba2199..8d1e8d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -613,20 +613,20 @@
 
     interface WakeUpListener {
         /** Called whenever the notifications are fully hidden or shown */
-        @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
+        fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
 
         /**
          * Called whenever the pulseExpansion changes
          *
          * @param expandingChanged if the user has started or stopped expanding
          */
-        @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
+        fun onPulseExpansionChanged(expandingChanged: Boolean) {}
 
         /**
          * Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
          * after the start delay, or after it ends/is cancelled.
          */
-        @JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
+        fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
     }
 
     companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 1cf9c1e..8029f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -3,10 +3,10 @@
 import android.util.FloatProperty
 import android.view.View
 import androidx.annotation.FloatRange
-import com.android.systemui.Dependency
 import com.android.systemui.R
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.ViewRefactorFlag
 import com.android.systemui.statusbar.notification.stack.AnimationProperties
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import kotlin.math.abs
@@ -27,37 +27,31 @@
 
     /** Current top roundness */
     @get:FloatRange(from = 0.0, to = 1.0)
-    @JvmDefault
     val topRoundness: Float
         get() = roundableState.topRoundness
 
     /** Current bottom roundness */
     @get:FloatRange(from = 0.0, to = 1.0)
-    @JvmDefault
     val bottomRoundness: Float
         get() = roundableState.bottomRoundness
 
     /** Max radius in pixel */
-    @JvmDefault
     val maxRadius: Float
         get() = roundableState.maxRadius
 
     /** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
-    @JvmDefault
     val topCornerRadius: Float
         get() =
-            if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius
+            if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
             else topRoundness * maxRadius
 
     /** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
-    @JvmDefault
     val bottomCornerRadius: Float
         get() =
-            if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius
+            if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
             else bottomRoundness * maxRadius
 
     /** Get and update the current radii */
-    @JvmDefault
     val updatedRadii: FloatArray
         get() =
             roundableState.radiiBuffer.also { radii ->
@@ -80,7 +74,6 @@
      * @param sourceType the source from which the request for roundness comes.
      * @return Whether the roundness was changed.
      */
-    @JvmDefault
     fun requestTopRoundness(
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         sourceType: SourceType,
@@ -125,7 +118,6 @@
      * @param sourceType the source from which the request for roundness comes.
      * @return Whether the roundness was changed.
      */
-    @JvmDefault
     fun requestTopRoundness(
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         sourceType: SourceType,
@@ -149,7 +141,6 @@
      * @param sourceType the source from which the request for roundness comes.
      * @return Whether the roundness was changed.
      */
-    @JvmDefault
     fun requestBottomRoundness(
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         sourceType: SourceType,
@@ -194,7 +185,6 @@
      * @param sourceType the source from which the request for roundness comes.
      * @return Whether the roundness was changed.
      */
-    @JvmDefault
     fun requestBottomRoundness(
         @FloatRange(from = 0.0, to = 1.0) value: Float,
         sourceType: SourceType,
@@ -219,7 +209,6 @@
      * @param animate true if it should animate to that value.
      * @return Whether the roundness was changed.
      */
-    @JvmDefault
     fun requestRoundness(
         @FloatRange(from = 0.0, to = 1.0) top: Float,
         @FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -246,7 +235,6 @@
      * @param sourceType the source from which the request for roundness comes.
      * @return Whether the roundness was changed.
      */
-    @JvmDefault
     fun requestRoundness(
         @FloatRange(from = 0.0, to = 1.0) top: Float,
         @FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -270,7 +258,6 @@
      * @param sourceType the source from which the request for roundness comes.
      * @param animate true if it should animate to that value.
      */
-    @JvmDefault
     fun requestRoundnessReset(sourceType: SourceType, animate: Boolean) {
         requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType, animate = animate)
     }
@@ -284,19 +271,16 @@
      *
      * @param sourceType the source from which the request for roundness comes.
      */
-    @JvmDefault
     fun requestRoundnessReset(sourceType: SourceType) {
         requestRoundnessReset(sourceType = sourceType, animate = roundableState.targetView.isShown)
     }
 
     /** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
-    @JvmDefault
     fun applyRoundnessAndInvalidate() {
         roundableState.targetView.invalidate()
     }
 
     /** @return true if top or bottom roundness is not zero. */
-    @JvmDefault
     fun hasRoundedCorner(): Boolean {
         return topRoundness != 0f || bottomRoundness != 0f
     }
@@ -307,7 +291,6 @@
      *
      * This method reuses the previous [radii] for performance reasons.
      */
-    @JvmDefault
     fun updateRadii(
         topCornerRadius: Float,
         bottomCornerRadius: Float,
@@ -335,13 +318,12 @@
     internal val targetView: View,
     private val roundable: Roundable,
     maxRadius: Float,
-    private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java)
+    featureFlags: FeatureFlags? = null
 ) {
     internal var maxRadius = maxRadius
         private set
 
-    internal val newHeadsUpAnimFlagEnabled
-        get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS)
+    internal val newHeadsUpAnim = ViewRefactorFlag(featureFlags, Flags.IMPROVED_HUN_ANIMATIONS)
 
     /** Animatable for top roundness */
     private val topAnimatable = topAnimatable(roundable)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 7898736..affd2d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -791,28 +791,6 @@
         return !mSbn.isOngoing() || !isLocked;
     }
 
-    /**
-     * @return Can the underlying notification be individually dismissed?
-     * @see #canViewBeDismissed()
-     */
-    // TODO: This logic doesn't belong on NotificationEntry. It should be moved to a controller
-    // that can be added as a dependency to any class that needs to answer this question.
-    public boolean legacyIsDismissableRecursive() {
-        if  (mSbn.isOngoing()) {
-            return false;
-        }
-        List<NotificationEntry> children = getAttachedNotifChildren();
-        if (children != null && children.size() > 0) {
-            for (int i = 0; i < children.size(); i++) {
-                NotificationEntry child =  children.get(i);
-                if (child.getSbn().isOngoing()) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
     public boolean canViewBeDismissed() {
         if (row == null) return true;
         return row.canViewBeDismissed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 1494574..93a34af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -48,6 +48,14 @@
     private val mAlwaysExpandNonGroupedNotification =
         context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications)
 
+    /**
+     * `true` if the first non-group expandable notification should be expanded automatically
+     * when possible. If `false`, then the first non-group expandable notification should not
+     * be expanded.
+     */
+    private val mAutoExpandFirstNotification =
+            context.resources.getBoolean(R.bool.config_autoExpandFirstNotification)
+
     override fun attach(pipeline: NotifPipeline) {
         pipeline.addOnBeforeRenderListListener(::onBeforeRenderList)
         pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
@@ -61,8 +69,10 @@
 
     private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
         // If mAlwaysExpandNonGroupedNotification is false, then only expand the
-        // very first notification and if it's not a child of grouped notifications.
-        controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || entry == entryToExpand)
+        // very first notification if it's not a child of grouped notifications and when
+        // mAutoExpandFirstNotification is true.
+        controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification ||
+                (mAutoExpandFirstNotification && entry == entryToExpand))
         // Show/hide the feedback icon
         controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
         // Show the "alerted" bell icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 1896080..9ecf50e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.collection.inflation;
 
-import static com.android.systemui.flags.Flags.NOTIFICATION_INLINE_REPLY_ANIMATION;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -176,8 +175,6 @@
         entry.setRow(row);
         mNotifBindPipeline.manageRow(entry, row);
         mPresenter.onBindRow(row);
-        row.setInlineReplyAnimationFlagEnabled(
-                mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION));
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
index b318252..78e9a74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.withIncreasedIndent
@@ -28,9 +27,7 @@
 import javax.inject.Inject
 
 @SysUISingleton
-class NotificationDismissibilityProviderImpl
-@Inject
-constructor(private val notifPipelineFlags: NotifPipelineFlags, dumpManager: DumpManager) :
+class NotificationDismissibilityProviderImpl @Inject constructor(dumpManager: DumpManager) :
     NotificationDismissibilityProvider, Dumpable {
 
     init {
@@ -43,11 +40,7 @@
         private set
 
     override fun isDismissable(entry: NotificationEntry): Boolean {
-        return if (notifPipelineFlags.allowDismissOngoing()) {
-            entry.key !in nonDismissableEntryKeys
-        } else {
-            entry.legacyIsDismissableRecursive()
-        }
+        return entry.key !in nonDismissableEntryKeys
     }
 
     @Synchronized
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 908c11a..36a8e98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -566,7 +566,7 @@
 
     @Override
     public float getTopCornerRadius() {
-        if (isNewHeadsUpAnimFlagEnabled()) {
+        if (mImprovedHunAnimation.isEnabled()) {
             return super.getTopCornerRadius();
         }
 
@@ -576,7 +576,7 @@
 
     @Override
     public float getBottomCornerRadius() {
-        if (isNewHeadsUpAnimFlagEnabled()) {
+        if (mImprovedHunAnimation.isEnabled()) {
             return super.getBottomCornerRadius();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b34c281..42b99a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -77,6 +77,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -275,7 +276,8 @@
     private OnExpandClickListener mOnExpandClickListener;
     private View.OnClickListener mOnFeedbackClickListener;
     private Path mExpandingClipPath;
-    private boolean mIsInlineReplyAnimationFlagEnabled = false;
+    private final ViewRefactorFlag mInlineReplyAnimation =
+            new ViewRefactorFlag(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
 
     // Listener will be called when receiving a long click event.
     // Use #setLongPressPosition to optionally assign positional data with the long press.
@@ -3121,10 +3123,6 @@
         return showingLayout != null && showingLayout.requireRowToHaveOverlappingRendering();
     }
 
-    public void setInlineReplyAnimationFlagEnabled(boolean isEnabled) {
-        mIsInlineReplyAnimationFlagEnabled = isEnabled;
-    }
-
     @Override
     public void setActualHeight(int height, boolean notifyListeners) {
         boolean changed = height != getActualHeight();
@@ -3144,7 +3142,7 @@
         }
         int contentHeight = Math.max(getMinHeight(), height);
         for (NotificationContentView l : mLayouts) {
-            if (mIsInlineReplyAnimationFlagEnabled) {
+            if (mInlineReplyAnimation.isEnabled()) {
                 l.setContentHeight(height);
             } else {
                 l.setContentHeight(contentHeight);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 7f23c1b..c8f13a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -28,10 +28,9 @@
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
 import com.android.systemui.statusbar.notification.RoundableState;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
 import com.android.systemui.util.DumpUtilsKt;
@@ -50,7 +49,8 @@
     private float mOutlineAlpha = -1f;
     private boolean mAlwaysRoundBothCorners;
     private Path mTmpPath = new Path();
-    private final FeatureFlags mFeatureFlags;
+    protected final ViewRefactorFlag mImprovedHunAnimation =
+            new ViewRefactorFlag(Flags.IMPROVED_HUN_ANIMATIONS);
 
     /**
      * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when
@@ -126,7 +126,7 @@
             return EMPTY_PATH;
         }
         float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius();
-        if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) {
+        if (!mImprovedHunAnimation.isEnabled() && (topRadius + bottomRadius > height)) {
             float overShoot = topRadius + bottomRadius - height;
             float currentTopRoundness = getTopRoundness();
             float currentBottomRoundness = getBottomRoundness();
@@ -167,7 +167,6 @@
         super(context, attrs);
         setOutlineProvider(mProvider);
         initDimens();
-        mFeatureFlags = Dependency.get(FeatureFlags.class);
     }
 
     @Override
@@ -376,8 +375,4 @@
         });
     }
 
-    // TODO(b/290365128) replace with ViewRefactorFlag
-    protected boolean isNewHeadsUpAnimFlagEnabled() {
-        return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
index 23a58d2..22a87a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt
@@ -21,7 +21,6 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl
@@ -66,7 +65,7 @@
     override fun setOnClickListener(listener: View.OnClickListener) = unsupported
 
     private val unsupported: Nothing
-        get() = NotificationShelfController.throwIllegalFlagStateError(expected = true)
+        get() = error("Code path not supported when NOTIFICATION_SHELF_REFACTOR is disabled")
 }
 
 /** Binds a [NotificationShelf] to its [view model][NotificationShelfViewModel]. */
@@ -80,8 +79,6 @@
     ) {
         ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager)
         shelf.apply {
-            setRefactorFlagEnabled(true)
-            setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM))
             // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind()
             notificationIconAreaController.setShelfIcons(shelfIcons)
             repeatWhenAttached {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index b0f3f59..95e74f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -29,7 +29,6 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
@@ -57,7 +56,6 @@
     private final SectionProvider mSectionProvider;
     private final BypassController mBypassController;
     private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
-    private final FeatureFlags mFeatureFlags;
     /**
      *  Used to read bouncer states.
      */
@@ -261,13 +259,12 @@
             @NonNull SectionProvider sectionProvider,
             @NonNull BypassController bypassController,
             @Nullable StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator,
-            @NonNull FeatureFlags featureFlags) {
+            @NonNull LargeScreenShadeInterpolator largeScreenShadeInterpolator
+    ) {
         mSectionProvider = sectionProvider;
         mBypassController = bypassController;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
-        mFeatureFlags = featureFlags;
         reload(context);
         dumpManager.registerDumpable(this);
     }
@@ -753,10 +750,6 @@
         return mLargeScreenShadeInterpolator;
     }
 
-    public FeatureFlags getFeatureFlags() {
-        return mFeatureFlags;
-    }
-
     @Override
     public void dump(PrintWriter pw, String[] args) {
         pw.println("mTopPadding=" + mTopPadding);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c1ceb3c..d71bc2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -89,6 +89,7 @@
 import com.android.systemui.R;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.shade.ShadeController;
@@ -198,7 +199,8 @@
     private Set<Integer> mDebugTextUsedYPositions;
     private final boolean mDebugRemoveAnimation;
     private final boolean mSensitiveRevealAnimEndabled;
-    private boolean mAnimatedInsets;
+    private final ViewRefactorFlag mAnimatedInsets;
+    private final ViewRefactorFlag mShelfRefactor;
 
     private int mContentHeight;
     private float mIntrinsicContentHeight;
@@ -621,7 +623,9 @@
         mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
         mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
         mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
-        setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS));
+        mAnimatedInsets =
+                new ViewRefactorFlag(featureFlags, Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+        mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
         mSectionsManager = Dependency.get(NotificationSectionsManager.class);
         mScreenOffAnimationController =
                 Dependency.get(ScreenOffAnimationController.class);
@@ -660,7 +664,7 @@
         mGroupMembershipManager = Dependency.get(GroupMembershipManager.class);
         mGroupExpansionManager = Dependency.get(GroupExpansionManager.class);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-        if (mAnimatedInsets) {
+        if (mAnimatedInsets.isEnabled()) {
             setWindowInsetsAnimationCallback(mInsetsCallback);
         }
     }
@@ -730,11 +734,6 @@
     }
 
     @VisibleForTesting
-    void setAnimatedInsetsEnabled(boolean enabled) {
-        mAnimatedInsets = enabled;
-    }
-
-    @VisibleForTesting
     public void updateFooter() {
         if (mFooterView == null) {
             return;
@@ -1773,7 +1772,7 @@
             return;
         }
         mForcedScroll = v;
-        if (mAnimatedInsets) {
+        if (mAnimatedInsets.isEnabled()) {
             updateForcedScroll();
         } else {
             scrollTo(v);
@@ -1822,7 +1821,7 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (!mAnimatedInsets) {
+        if (!mAnimatedInsets.isEnabled()) {
             mBottomInset = insets.getInsets(WindowInsets.Type.ime()).bottom;
         }
         mWaterfallTopInset = 0;
@@ -1830,11 +1829,11 @@
         if (cutout != null) {
             mWaterfallTopInset = cutout.getWaterfallInsets().top;
         }
-        if (mAnimatedInsets && !mIsInsetAnimationRunning) {
+        if (mAnimatedInsets.isEnabled() && !mIsInsetAnimationRunning) {
             // update bottom inset e.g. after rotation
             updateBottomInset(insets);
         }
-        if (!mAnimatedInsets) {
+        if (!mAnimatedInsets.isEnabled()) {
             int range = getScrollRange();
             if (mOwnScrollY > range) {
                 // HACK: We're repeatedly getting staggered insets here while the IME is
@@ -2714,7 +2713,7 @@
      * @param listener callback for notification removed
      */
     public void setOnNotificationRemovedListener(OnNotificationRemovedListener listener) {
-        NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+        mShelfRefactor.assertDisabled();
         mOnNotificationRemovedListener = listener;
     }
 
@@ -2727,7 +2726,7 @@
         if (!mChildTransferInProgress) {
             onViewRemovedInternal(expandableView, this);
         }
-        if (mAmbientState.getFeatureFlags().isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+        if (mShelfRefactor.isEnabled()) {
             mShelf.requestRoundnessResetFor(expandableView);
         } else {
             if (mOnNotificationRemovedListener != null) {
@@ -4943,18 +4942,12 @@
 
     @Nullable
     public ExpandableView getShelf() {
-        if (NotificationShelfController.checkRefactorFlagEnabled(mAmbientState.getFeatureFlags())) {
-            return mShelf;
-        } else {
-            return null;
-        }
+        if (!mShelfRefactor.expectEnabled()) return null;
+        return mShelf;
     }
 
     public void setShelf(NotificationShelf shelf) {
-        if (!NotificationShelfController.checkRefactorFlagEnabled(
-                mAmbientState.getFeatureFlags())) {
-            return;
-        }
+        if (!mShelfRefactor.expectEnabled()) return;
         int index = -1;
         if (mShelf != null) {
             index = indexOfChild(mShelf);
@@ -4968,7 +4961,7 @@
     }
 
     public void setShelfController(NotificationShelfController notificationShelfController) {
-        NotificationShelfController.assertRefactorFlagDisabled(mAmbientState.getFeatureFlags());
+        mShelfRefactor.assertDisabled();
         int index = -1;
         if (mShelf != null) {
             index = indexOfChild(mShelf);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ef7375a..4668aa4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -65,6 +65,7 @@
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -205,6 +206,7 @@
     private boolean mIsInTransitionToAod = false;
 
     private final FeatureFlags mFeatureFlags;
+    private final ViewRefactorFlag mShelfRefactor;
     private final NotificationTargetsHelper mNotificationTargetsHelper;
     private final SecureSettings mSecureSettings;
     private final NotificationDismissibilityProvider mDismissibilityProvider;
@@ -718,6 +720,7 @@
         mShadeController = shadeController;
         mNotifIconAreaController = notifIconAreaController;
         mFeatureFlags = featureFlags;
+        mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
         mNotificationTargetsHelper = notificationTargetsHelper;
         mSecureSettings = secureSettings;
         mDismissibilityProvider = dismissibilityProvider;
@@ -1432,7 +1435,7 @@
     }
 
     public void setShelfController(NotificationShelfController notificationShelfController) {
-        NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+        mShelfRefactor.assertDisabled();
         mView.setShelfController(notificationShelfController);
     }
 
@@ -1645,12 +1648,12 @@
     }
 
     public void setShelf(NotificationShelf shelf) {
-        if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) return;
+        if (!mShelfRefactor.expectEnabled()) return;
         mView.setShelf(shelf);
     }
 
     public int getShelfHeight() {
-        if (!NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+        if (!mShelfRefactor.expectEnabled()) {
             return 0;
         }
         ExpandableView shelf = mView.getShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 2b9c3d3..acd6e49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -259,8 +259,6 @@
 
     void readyForKeyguardDone();
 
-    void setLockscreenUser(int newUserId);
-
     void showKeyguard();
 
     boolean hideKeyguard();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 5fb729c..c3c9a61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -51,7 +51,6 @@
 import android.app.StatusBarManager;
 import android.app.TaskInfo;
 import android.app.UiModeManager;
-import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -980,16 +979,6 @@
 
         createAndAddWindows(result);
 
-        if (mWallpaperSupported) {
-            // Make sure we always have the most current wallpaper info.
-            IntentFilter wallpaperChangedFilter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
-            mBroadcastDispatcher.registerReceiver(mWallpaperChangedReceiver, wallpaperChangedFilter,
-                    null /* handler */, UserHandle.ALL);
-            mWallpaperChangedReceiver.onReceive(mContext, null);
-        } else if (DEBUG) {
-            Log.v(TAG, "start(): no wallpaper service ");
-        }
-
         // Set up the initial notification state. This needs to happen before CommandQueue.disable()
         setUpPresenter();
 
@@ -2072,6 +2061,7 @@
     void updateDisplaySize() {
         mDisplay.getMetrics(mDisplayMetrics);
         mDisplay.getSize(mCurrentDisplaySize);
+        mMediaManager.onDisplayUpdated(mDisplay);
         if (DEBUG_GESTURES) {
             mGestureRec.tag("display",
                     String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
@@ -2164,18 +2154,6 @@
     };
 
     /**
-     * Notify the shade controller that the current user changed
-     *
-     * @param newUserId userId of the new user
-     */
-    @Override
-    public void setLockscreenUser(int newUserId) {
-        if (mWallpaperSupported) {
-            mWallpaperChangedReceiver.onReceive(mContext, null);
-        }
-    }
-
-    /**
      * Reload some of our resources when the configuration changes.
      *
      * We don't reload everything when the configuration changes -- we probably
@@ -3549,33 +3527,6 @@
         }
     };
 
-    /**
-     * @deprecated See {@link com.android.systemui.wallpapers.data.repository.WallpaperRepository}
-     * instead.
-     */
-    private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!mWallpaperSupported) {
-                // Receiver should not have been registered at all...
-                Log.wtf(TAG, "WallpaperManager not supported");
-                return;
-            }
-            WallpaperInfo info = mWallpaperManager.getWallpaperInfoForUser(
-                    mUserTracker.getUserId());
-            mWallpaperController.onWallpaperInfoUpdated(info);
-
-            final boolean deviceSupportsAodWallpaper = mContext.getResources().getBoolean(
-                    com.android.internal.R.bool.config_dozeSupportsAodWallpaper);
-            // If WallpaperInfo is null, it must be ImageWallpaper.
-            final boolean supportsAmbientMode = deviceSupportsAodWallpaper
-                    && (info != null && info.supportsAmbientMode());
-
-            mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
-            mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode);
-        }
-    };
-
     private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
         @Override
         public void onConfigChanged(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 8e9f382..374543d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
@@ -34,10 +33,7 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
-import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
@@ -52,7 +48,6 @@
     private static final String TAG = "DemoStatusIcons";
 
     private final LinearLayout mStatusIcons;
-    private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
     private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>();
     private final int mIconSize;
 
@@ -91,7 +86,6 @@
     }
 
     public void remove() {
-        mMobileViews.clear();
         ((ViewGroup) getParent()).removeView(this);
     }
 
@@ -127,7 +121,6 @@
         mDemoMode = false;
         mStatusIcons.setVisibility(View.VISIBLE);
         mModernMobileViews.clear();
-        mMobileViews.clear();
         setVisibility(View.GONE);
     }
 
@@ -236,22 +229,6 @@
     }
 
     /**
-     * Add a new mobile icon view
-     */
-    public void addMobileView(MobileIconState state, Context mobileContext) {
-        Log.d(TAG, "addMobileView: ");
-        StatusBarMobileView view = StatusBarMobileView
-                .fromContext(mobileContext, state.slot);
-
-        view.applyMobileState(state);
-        view.setStaticDrawableColor(mColor);
-
-        // mobile always goes at the end
-        mMobileViews.add(view);
-        addView(view, getChildCount(), createLayoutParams());
-    }
-
-    /**
      * Add a {@link ModernStatusBarMobileView}
      * @param mobileContext possibly mcc/mnc overridden mobile context
      * @param subId the subscriptionId for this mobile view
@@ -285,8 +262,7 @@
         // If we have mobile views, put wifi before them
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
-            if (child instanceof StatusBarMobileView
-                    || child instanceof ModernStatusBarMobileView) {
+            if (child instanceof ModernStatusBarMobileView) {
                 viewIndex = i;
                 break;
             }
@@ -297,26 +273,6 @@
         addView(view, viewIndex, createLayoutParams());
     }
 
-    /**
-     * Apply an update to a mobile icon view for the given {@link MobileIconState}. For
-     * compatibility with {@link MobileContextProvider}, we have to recreate the view every time we
-     * update it, since the context (and thus the {@link Configuration}) may have changed
-     */
-    public void updateMobileState(MobileIconState state, Context mobileContext) {
-        Log.d(TAG, "updateMobileState: " + state);
-
-        // The mobile config provided by MobileContextProvider could have changed; always recreate
-        for (int i = 0; i < mMobileViews.size(); i++) {
-            StatusBarMobileView view = mMobileViews.get(i);
-            if (view.getState().subId == state.subId) {
-                removeView(view);
-            }
-        }
-
-        // Add the replacement or new icon
-        addMobileView(state, mobileContext);
-    }
-
     public void onRemoveIcon(StatusIconDisplayable view) {
         if (view.getSlot().equals("wifi")) {
             if (view instanceof ModernStatusBarWifiView) {
@@ -324,12 +280,6 @@
                 removeView(mModernWifiView);
                 mModernWifiView = null;
             }
-        } else if (view instanceof StatusBarMobileView) {
-            StatusBarMobileView mobileView = matchingMobileView(view);
-            if (mobileView != null) {
-                removeView(mobileView);
-                mMobileViews.remove(mobileView);
-            }
         } else if (view instanceof ModernStatusBarMobileView) {
             ModernStatusBarMobileView mobileView = matchingModernMobileView(
                     (ModernStatusBarMobileView) view);
@@ -340,21 +290,6 @@
         }
     }
 
-    private StatusBarMobileView matchingMobileView(StatusIconDisplayable otherView) {
-        if (!(otherView instanceof StatusBarMobileView)) {
-            return null;
-        }
-
-        StatusBarMobileView v = (StatusBarMobileView) otherView;
-        for (StatusBarMobileView view : mMobileViews) {
-            if (view.getState().subId == v.getState().subId) {
-                return view;
-            }
-        }
-
-        return null;
-    }
-
     private ModernStatusBarMobileView matchingModernMobileView(ModernStatusBarMobileView other) {
         for (ModernStatusBarMobileView v : mModernMobileViews) {
             if (v.getSubId() == other.getSubId()) {
@@ -376,9 +311,6 @@
         if (mModernWifiView != null) {
             mModernWifiView.onDarkChanged(areas, darkIntensity, tint);
         }
-        for (StatusBarMobileView view : mMobileViews) {
-            view.onDarkChanged(areas, darkIntensity, tint);
-        }
         for (ModernStatusBarMobileView view : mModernMobileViews) {
             view.onDarkChanged(areas, darkIntensity, tint);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 7312db6..ed9722e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -311,6 +311,7 @@
 
     @Override
     public void dozeTimeTick() {
+        mDozeInteractor.dozeTimeTick();
         mNotificationPanel.dozeTimeTick();
         mAuthController.dozeTimeTick();
         if (mAmbientIndicationContainer instanceof DozeReceiver) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index b2c39f7..92c786f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -306,19 +306,25 @@
 
     /**
      * Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
+     *
+     * <p>Aligns to the center when showing on the smaller internal display of a multi display
+     * device.
      */
     public static class WallpaperDrawable extends DrawableWrapper {
 
         private final ConstantState mState;
         private final Rect mTmpRect = new Rect();
+        private boolean mIsOnSmallerInternalDisplays;
 
-        public WallpaperDrawable(Resources r, Bitmap b) {
-            this(r, new ConstantState(b));
+        public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) {
+            this(r, new ConstantState(b), isOnSmallerInternalDisplays);
         }
 
-        private WallpaperDrawable(Resources r, ConstantState state) {
+        private WallpaperDrawable(Resources r, ConstantState state,
+                boolean isOnSmallerInternalDisplays) {
             super(new BitmapDrawable(r, state.mBackground));
             mState = state;
+            mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
         }
 
         @Override
@@ -357,10 +363,17 @@
             }
             dy = (vheight - dheight * scale) * 0.5f;
 
+            int offsetX = 0;
+            // Offset to show the center area of the wallpaper on a smaller display for multi
+            // display device
+            if (mIsOnSmallerInternalDisplays) {
+                offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2);
+            }
+
             mTmpRect.set(
-                    bounds.left,
+                    bounds.left + offsetX,
                     bounds.top + Math.round(dy),
-                    bounds.left + Math.round(dwidth * scale),
+                    bounds.left + Math.round(dwidth * scale) + offsetX,
                     bounds.top + Math.round(dheight * scale + dy));
 
             super.onBoundsChange(mTmpRect);
@@ -371,6 +384,17 @@
             return mState;
         }
 
+        /**
+         * Update bounds when the hosting display or the display size has changed.
+         *
+         * @param isOnSmallerInternalDisplays true if the drawable is on one of the internal
+         *                                    displays with the smaller area.
+         */
+        public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) {
+            mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
+            onBoundsChange(getBounds());
+        }
+
         static class ConstantState extends Drawable.ConstantState {
 
             private final Bitmap mBackground;
@@ -386,7 +410,7 @@
 
             @Override
             public Drawable newDrawable(@Nullable Resources res) {
-                return new WallpaperDrawable(res, this);
+                return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false);
             }
 
             @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index e18c9d8..0bf0f4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -24,6 +24,8 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.flags.ViewRefactorFlag;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -88,7 +90,7 @@
     private final ArrayList<Rect> mTintAreas = new ArrayList<>();
     private final Context mContext;
 
-    private final FeatureFlags mFeatureFlags;
+    private final ViewRefactorFlag mShelfRefactor;
 
     private int mAodIconAppearTranslation;
 
@@ -120,12 +122,13 @@
             Optional<Bubbles> bubblesOptional,
             DemoModeController demoModeController,
             DarkIconDispatcher darkIconDispatcher,
-            FeatureFlags featureFlags, StatusBarWindowController statusBarWindowController,
+            FeatureFlags featureFlags,
+            StatusBarWindowController statusBarWindowController,
             ScreenOffAnimationController screenOffAnimationController) {
         mContrastColorUtil = ContrastColorUtil.getInstance(context);
         mContext = context;
         mStatusBarStateController = statusBarStateController;
-        mFeatureFlags = featureFlags;
+        mShelfRefactor = new ViewRefactorFlag(featureFlags, Flags.NOTIFICATION_SHELF_REFACTOR);
         mStatusBarStateController.addCallback(this);
         mMediaManager = notificationMediaManager;
         mDozeParameters = dozeParameters;
@@ -179,12 +182,12 @@
     }
 
     public void setupShelf(NotificationShelfController notificationShelfController) {
-        NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
+        mShelfRefactor.assertDisabled();
         mShelfIcons = notificationShelfController.getShelfIcons();
     }
 
     public void setShelfIcons(NotificationIconContainer icons) {
-        if (NotificationShelfController.checkRefactorFlagEnabled(mFeatureFlags)) {
+        if (mShelfRefactor.expectEnabled()) {
             mShelfIcons = icons;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 1d934d6..79151fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -548,7 +548,7 @@
                 final int iconResId = mUserManager.getUserStatusBarIconResId(userId);
                 // TODO(b/170249807, b/230779281): Handle non-managed-profile String
                 String accessibilityString = getManagedProfileAccessibilityString();
-                mHandler.post(() -> {
+                mMainExecutor.execute(() -> {
                     final boolean showIcon;
                     if (iconResId != Resources.ID_NULL && (!mKeyguardStateController.isShowing()
                             || mKeyguardStateController.isOccluded())) {
@@ -629,6 +629,13 @@
     }
 
     @Override
+    public void appTransitionFinished(int displayId) {
+        if (mDisplayId == displayId) {
+            updateProfileIcon();
+        }
+    }
+
+    @Override
     public void onKeyguardShowingChanged() {
         updateProfileIcon();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 42b0a4f..d5cb6b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -15,7 +15,6 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
-import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW;
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW;
 
@@ -24,10 +23,8 @@
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.ArraySet;
-import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
 
@@ -41,12 +38,9 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
 import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
@@ -100,13 +94,10 @@
      */
     void setNewWifiIcon();
 
-    /** */
-    void setMobileIcons(String slot, List<MobileIconState> states);
-
     /**
-     * This method completely replaces {@link #setMobileIcons} with the information from the new
-     * mobile data pipeline. Icons will automatically keep their state up to date, so we don't have
-     * to worry about funneling MobileIconState objects through anymore.
+     * Notify this class that there is a new set of mobile icons to display, keyed off of this list
+     * of subIds. The icons will be added and bound to the mobile data pipeline via
+     * {@link com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder}.
      */
     void setNewMobileIconSubIds(List<Integer> subIds);
     /**
@@ -168,14 +159,12 @@
         public DarkIconManager(
                 LinearLayout linearLayout,
                 StatusBarLocation location,
-                StatusBarPipelineFlags statusBarPipelineFlags,
                 WifiUiAdapter wifiUiAdapter,
                 MobileUiAdapter mobileUiAdapter,
                 MobileContextProvider mobileContextProvider,
                 DarkIconDispatcher darkIconDispatcher) {
             super(linearLayout,
                     location,
-                    statusBarPipelineFlags,
                     wifiUiAdapter,
                     mobileUiAdapter,
                     mobileContextProvider);
@@ -235,7 +224,6 @@
 
         @SysUISingleton
         public static class Factory {
-            private final StatusBarPipelineFlags mStatusBarPipelineFlags;
             private final WifiUiAdapter mWifiUiAdapter;
             private final MobileContextProvider mMobileContextProvider;
             private final MobileUiAdapter mMobileUiAdapter;
@@ -243,12 +231,10 @@
 
             @Inject
             public Factory(
-                    StatusBarPipelineFlags statusBarPipelineFlags,
                     WifiUiAdapter wifiUiAdapter,
                     MobileContextProvider mobileContextProvider,
                     MobileUiAdapter mobileUiAdapter,
                     DarkIconDispatcher darkIconDispatcher) {
-                mStatusBarPipelineFlags = statusBarPipelineFlags;
                 mWifiUiAdapter = wifiUiAdapter;
                 mMobileContextProvider = mobileContextProvider;
                 mMobileUiAdapter = mobileUiAdapter;
@@ -259,7 +245,6 @@
                 return new DarkIconManager(
                         group,
                         location,
-                        mStatusBarPipelineFlags,
                         mWifiUiAdapter,
                         mMobileUiAdapter,
                         mMobileContextProvider,
@@ -277,14 +262,12 @@
         public TintedIconManager(
                 ViewGroup group,
                 StatusBarLocation location,
-                StatusBarPipelineFlags statusBarPipelineFlags,
                 WifiUiAdapter wifiUiAdapter,
                 MobileUiAdapter mobileUiAdapter,
                 MobileContextProvider mobileContextProvider
         ) {
             super(group,
                     location,
-                    statusBarPipelineFlags,
                     wifiUiAdapter,
                     mobileUiAdapter,
                     mobileContextProvider);
@@ -319,19 +302,16 @@
 
         @SysUISingleton
         public static class Factory {
-            private final StatusBarPipelineFlags mStatusBarPipelineFlags;
             private final WifiUiAdapter mWifiUiAdapter;
             private final MobileContextProvider mMobileContextProvider;
             private final MobileUiAdapter mMobileUiAdapter;
 
             @Inject
             public Factory(
-                    StatusBarPipelineFlags statusBarPipelineFlags,
                     WifiUiAdapter wifiUiAdapter,
                     MobileUiAdapter mobileUiAdapter,
                     MobileContextProvider mobileContextProvider
             ) {
-                mStatusBarPipelineFlags = statusBarPipelineFlags;
                 mWifiUiAdapter = wifiUiAdapter;
                 mMobileUiAdapter = mobileUiAdapter;
                 mMobileContextProvider = mobileContextProvider;
@@ -341,7 +321,6 @@
                 return new TintedIconManager(
                         group,
                         location,
-                        mStatusBarPipelineFlags,
                         mWifiUiAdapter,
                         mMobileUiAdapter,
                         mMobileContextProvider);
@@ -354,7 +333,6 @@
      */
     class IconManager implements DemoModeCommandReceiver {
         protected final ViewGroup mGroup;
-        private final StatusBarPipelineFlags mStatusBarPipelineFlags;
         private final MobileContextProvider mMobileContextProvider;
         private final LocationBasedWifiViewModel mWifiViewModel;
         private final MobileIconsViewModel mMobileIconsViewModel;
@@ -376,27 +354,21 @@
         public IconManager(
                 ViewGroup group,
                 StatusBarLocation location,
-                StatusBarPipelineFlags statusBarPipelineFlags,
                 WifiUiAdapter wifiUiAdapter,
                 MobileUiAdapter mobileUiAdapter,
                 MobileContextProvider mobileContextProvider
         ) {
             mGroup = group;
-            mStatusBarPipelineFlags = statusBarPipelineFlags;
             mMobileContextProvider = mobileContextProvider;
             mContext = group.getContext();
             mLocation = location;
 
             reloadDimens();
 
-            if (statusBarPipelineFlags.runNewMobileIconsBackend()) {
-                // This starts the flow for the new pipeline, and will notify us of changes if
-                // {@link StatusBarPipelineFlags#useNewMobileIcons} is also true.
-                mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
-                MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
-            } else {
-                mMobileIconsViewModel = null;
-            }
+            // This starts the flow for the new pipeline, and will notify us of changes via
+            // {@link #setNewMobileIconIds}
+            mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
+            MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
 
             mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation);
         }
@@ -449,9 +421,6 @@
                 case TYPE_WIFI_NEW:
                     return addNewWifiIcon(index, slot);
 
-                case TYPE_MOBILE:
-                    return addMobileIcon(index, slot, holder.getMobileState());
-
                 case TYPE_MOBILE_NEW:
                     return addNewMobileIcon(index, slot, holder.getTag());
             }
@@ -479,40 +448,12 @@
             return view;
         }
 
-        @VisibleForTesting
-        protected StatusIconDisplayable addMobileIcon(
-                int index,
-                String slot,
-                MobileIconState state
-        ) {
-            if (mStatusBarPipelineFlags.useNewMobileIcons()) {
-                throw new IllegalStateException("Attempting to add a mobile icon while the new "
-                        + "icons are enabled is not supported");
-            }
-
-            // Use the `subId` field as a key to query for the correct context
-            StatusBarMobileView mobileView = onCreateStatusBarMobileView(state.subId, slot);
-            mobileView.applyMobileState(state);
-            mGroup.addView(mobileView, index, onCreateLayoutParams());
-
-            if (mIsInDemoMode) {
-                Context mobileContext = mMobileContextProvider
-                        .getMobileContextForSub(state.subId, mContext);
-                mDemoStatusIcons.addMobileView(state, mobileContext);
-            }
-            return mobileView;
-        }
 
         protected StatusIconDisplayable addNewMobileIcon(
                 int index,
                 String slot,
                 int subId
         ) {
-            if (!mStatusBarPipelineFlags.useNewMobileIcons()) {
-                throw new IllegalStateException("Attempting to add a mobile icon using the new"
-                        + "pipeline, but the enabled flag is false.");
-            }
-
             BaseStatusBarFrameLayout view = onCreateModernStatusBarMobileView(slot, subId);
             mGroup.addView(view, index, onCreateLayoutParams());
 
@@ -536,13 +477,6 @@
             return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel);
         }
 
-        private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) {
-            Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
-            StatusBarMobileView view = StatusBarMobileView
-                    .fromContext(mobileContext, slot);
-            return view;
-        }
-
         private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
                 String slot, int subId) {
             Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
@@ -568,15 +502,6 @@
                     com.android.internal.R.dimen.status_bar_icon_size_sp);
         }
 
-        private void setHeightAndCenter(ImageView imageView, int height) {
-            ViewGroup.LayoutParams params = imageView.getLayoutParams();
-            params.height = height;
-            if (params instanceof LinearLayout.LayoutParams) {
-                ((LinearLayout.LayoutParams) params).gravity = Gravity.CENTER_VERTICAL;
-            }
-            imageView.setLayoutParams(params);
-        }
-
         protected void onRemoveIcon(int viewIndex) {
             if (mIsInDemoMode) {
                 mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
@@ -594,9 +519,6 @@
                 case TYPE_ICON:
                     onSetIcon(viewIndex, holder.getIcon());
                     return;
-                case TYPE_MOBILE:
-                    onSetMobileIcon(viewIndex, holder.getMobileState());
-                    return;
                 case TYPE_MOBILE_NEW:
                 case TYPE_WIFI_NEW:
                     // Nothing, the new icons update themselves
@@ -606,23 +528,6 @@
             }
         }
 
-        public void onSetMobileIcon(int viewIndex, MobileIconState state) {
-            View view = mGroup.getChildAt(viewIndex);
-            if (view instanceof StatusBarMobileView) {
-                ((StatusBarMobileView) view).applyMobileState(state);
-            } else {
-                // ModernStatusBarMobileView automatically updates via the ViewModel
-                throw new IllegalStateException("Cannot update ModernStatusBarMobileView outside of"
-                        + "the new pipeline");
-            }
-
-            if (mIsInDemoMode) {
-                Context mobileContext = mMobileContextProvider
-                        .getMobileContextForSub(state.subId, mContext);
-                mDemoStatusIcons.updateMobileState(state, mobileContext);
-            }
-        }
-
         @Override
         public void dispatchDemoCommand(String command, Bundle args) {
             if (!mDemoable) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index d1a02d6..553cbc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -39,7 +39,6 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -215,41 +214,10 @@
     /**
      * Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted
      * by subId. Don't worry this definitely makes sense and works.
-     * @param slot da slot
-     * @param iconStates All of the mobile icon states
+     * @param subIds list of subscription ID integers that provide the key to the icon to display.
      */
     @Override
-    public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
-        if (mStatusBarPipelineFlags.useNewMobileIcons()) {
-            Log.d(TAG, "ignoring old pipeline callbacks, because the new mobile "
-                    + "icons are enabled");
-            return;
-        }
-        Slot mobileSlot = mStatusBarIconList.getSlot(slot);
-
-        // Reverse the sort order to show icons with left to right([Slot1][Slot2]..).
-        // StatusBarIconList has UI design that first items go to the right of second items.
-        Collections.reverse(iconStates);
-
-        for (MobileIconState state : iconStates) {
-            StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
-            if (holder == null) {
-                holder = StatusBarIconHolder.fromMobileIconState(state);
-                setIcon(slot, holder);
-            } else {
-                holder.setMobileState(state);
-                handleSet(slot, holder);
-            }
-        }
-    }
-
-    @Override
     public void setNewMobileIconSubIds(List<Integer> subIds) {
-        if (!mStatusBarPipelineFlags.useNewMobileIcons()) {
-            Log.d(TAG, "ignoring new pipeline callback, "
-                    + "since the new mobile icons are disabled");
-            return;
-        }
         String slotName = mContext.getString(com.android.internal.R.string.status_bar_mobile);
         Slot mobileSlot = mStatusBarIconList.getSlot(slotName);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index 01fd247..7048a78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -24,7 +24,6 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel;
 
 import java.lang.annotation.Retention;
@@ -35,7 +34,6 @@
  */
 public class StatusBarIconHolder {
     public static final int TYPE_ICON = 0;
-    public static final int TYPE_MOBILE = 2;
     /**
      * TODO (b/249790733): address this once the new pipeline is in place
      * This type exists so that the new pipeline (see {@link MobileIconViewModel}) can be used
@@ -63,7 +61,6 @@
 
     @IntDef({
             TYPE_ICON,
-            TYPE_MOBILE,
             TYPE_MOBILE_NEW,
             TYPE_WIFI_NEW
     })
@@ -71,7 +68,6 @@
     @interface IconType {}
 
     private StatusBarIcon mIcon;
-    private MobileIconState mMobileState;
     private @IconType int mType = TYPE_ICON;
     private int mTag = 0;
 
@@ -79,7 +75,6 @@
     public static String getTypeString(@IconType int type) {
         switch(type) {
             case TYPE_ICON: return "ICON";
-            case TYPE_MOBILE: return "MOBILE_OLD";
             case TYPE_MOBILE_NEW: return "MOBILE_NEW";
             case TYPE_WIFI_NEW: return "WIFI_NEW";
             default: return "UNKNOWN";
@@ -103,15 +98,6 @@
         return holder;
     }
 
-    /** */
-    public static StatusBarIconHolder fromMobileIconState(MobileIconState state) {
-        StatusBarIconHolder holder = new StatusBarIconHolder();
-        holder.mMobileState = state;
-        holder.mType = TYPE_MOBILE;
-        holder.mTag = state.subId;
-        return holder;
-    }
-
     /**
      * ONLY for use with the new connectivity pipeline, where we only need a subscriptionID to
      * determine icon ordering and building the correct view model
@@ -153,21 +139,10 @@
         mIcon = icon;
     }
 
-    @Nullable
-    public MobileIconState getMobileState() {
-        return mMobileState;
-    }
-
-    public void setMobileState(MobileIconState state) {
-        mMobileState = state;
-    }
-
     public boolean isVisible() {
         switch (mType) {
             case TYPE_ICON:
                 return mIcon.visible;
-            case TYPE_MOBILE:
-                return mMobileState.visible;
             case TYPE_MOBILE_NEW:
             case TYPE_WIFI_NEW:
                 // The new pipeline controls visibilities via the view model and view binder, so
@@ -188,10 +163,6 @@
                 mIcon.visible = visible;
                 break;
 
-            case TYPE_MOBILE:
-                mMobileState.visible = visible;
-                break;
-
             case TYPE_MOBILE_NEW:
             case TYPE_WIFI_NEW:
                 // The new pipeline controls visibilities via the view model and view binder, so
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index e63fecd..ad8530d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -79,7 +79,6 @@
     private final HeadsUpManagerPhone mHeadsUpManager;
     private final AboveShelfObserver mAboveShelfObserver;
     private final DozeScrimController mDozeScrimController;
-    private final CentralSurfaces mCentralSurfaces;
     private final NotificationsInteractor mNotificationsInteractor;
     private final NotificationStackScrollLayoutController mNsslController;
     private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -107,7 +106,6 @@
             NotificationShadeWindowController notificationShadeWindowController,
             DynamicPrivacyController dynamicPrivacyController,
             KeyguardStateController keyguardStateController,
-            CentralSurfaces centralSurfaces,
             NotificationsInteractor notificationsInteractor,
             LockscreenShadeTransitionController shadeTransitionController,
             PowerInteractor powerInteractor,
@@ -128,8 +126,6 @@
         mQsController = quickSettingsController;
         mHeadsUpManager = headsUp;
         mDynamicPrivacyController = dynamicPrivacyController;
-        // TODO: use KeyguardStateController#isOccluded to remove this dependency
-        mCentralSurfaces = centralSurfaces;
         mNotificationsInteractor = notificationsInteractor;
         mNsslController = stackScrollerController;
         mShadeTransitionController = shadeTransitionController;
@@ -208,7 +204,6 @@
         // End old BaseStatusBar.userSwitched
         mCommandQueue.animateCollapsePanels();
         mMediaManager.clearCurrentMediaNotification();
-        mCentralSurfaces.setLockscreenUser(newUserId);
         updateMediaMetaData(true, false);
     }
 
@@ -284,7 +279,7 @@
         @Override
         public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
             final StatusBarNotification sbn = entry.getSbn();
-            if (mCentralSurfaces.isOccluded()) {
+            if (mKeyguardStateController.isOccluded()) {
                 boolean devicePublic = mLockscreenUserManager
                         .isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
                 boolean userPublic = devicePublic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 6919996..344e56c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Handler;
-import android.telephony.SubscriptionInfo;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -27,7 +26,6 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.connectivity.IconState;
-import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
 import com.android.systemui.statusbar.connectivity.NetworkController;
 import com.android.systemui.statusbar.connectivity.SignalCallback;
 import com.android.systemui.statusbar.policy.SecurityController;
@@ -71,7 +69,6 @@
     // Track as little state as possible, and only for padding purposes
     private boolean mIsAirplaneMode = false;
 
-    private ArrayList<MobileIconState> mMobileStates = new ArrayList<>();
     private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
     private boolean mInitialized;
 
@@ -102,7 +99,7 @@
         mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
     }
 
-    /** Call to initilaize and register this classw with the system. */
+    /** Call to initialize and register this class with the system. */
     public void init() {
         if (mInitialized) {
             return;
@@ -189,34 +186,6 @@
                 CallIndicatorIconState.copyStates(mCallIndicatorStates));
     }
 
-    @Override
-    public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
-        if (DEBUG) {
-            Log.d(TAG, "setMobileDataIndicators: " + indicators);
-        }
-        MobileIconState state = getState(indicators.subId);
-        if (state == null) {
-            return;
-        }
-
-        state.visible = indicators.statusIcon.visible && !mHideMobile;
-        state.strengthId = indicators.statusIcon.icon;
-        state.typeId = indicators.statusType;
-        state.contentDescription = indicators.statusIcon.contentDescription;
-        state.typeContentDescription = indicators.typeContentDescription;
-        state.showTriangle = indicators.showTriangle;
-        state.roaming = indicators.roaming;
-        state.activityIn = indicators.activityIn && mActivityEnabled;
-        state.activityOut = indicators.activityOut && mActivityEnabled;
-
-        if (DEBUG) {
-            Log.d(TAG, "MobileIconStates: "
-                    + (mMobileStates == null ? "" : mMobileStates.toString()));
-        }
-        // Always send a copy to maintain value type semantics
-        mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
-    }
-
     private CallIndicatorIconState getNoCallingState(int subId) {
         for (CallIndicatorIconState state : mCallIndicatorStates) {
             if (state.subId == subId) {
@@ -227,74 +196,8 @@
         return null;
     }
 
-    private MobileIconState getState(int subId) {
-        for (MobileIconState state : mMobileStates) {
-            if (state.subId == subId) {
-                return state;
-            }
-        }
-        Log.e(TAG, "Unexpected subscription " + subId);
-        return null;
-    }
-
-    /**
-     * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators
-     * so we don't have to update the icon manager at this point, just remove the old ones
-     * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8)
-     */
-    @Override
-    public void setSubs(List<SubscriptionInfo> subs) {
-        if (DEBUG) Log.d(TAG, "setSubs: " + (subs == null ? "" : subs.toString()));
-        if (hasCorrectSubs(subs)) {
-            return;
-        }
-
-        mIconController.removeAllIconsForSlot(mSlotMobile);
-        mIconController.removeAllIconsForSlot(mSlotNoCalling);
-        mIconController.removeAllIconsForSlot(mSlotCallStrength);
-        mMobileStates.clear();
-        List<CallIndicatorIconState> noCallingStates = new ArrayList<CallIndicatorIconState>();
-        noCallingStates.addAll(mCallIndicatorStates);
-        mCallIndicatorStates.clear();
-        final int n = subs.size();
-        for (int i = 0; i < n; i++) {
-            mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
-            boolean isNewSub = true;
-            for (CallIndicatorIconState state : noCallingStates) {
-                if (state.subId == subs.get(i).getSubscriptionId()) {
-                    mCallIndicatorStates.add(state);
-                    isNewSub = false;
-                    break;
-                }
-            }
-            if (isNewSub) {
-                mCallIndicatorStates.add(
-                        new CallIndicatorIconState(subs.get(i).getSubscriptionId()));
-            }
-        }
-    }
-
-    private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
-        final int N = subs.size();
-        if (N != mMobileStates.size()) {
-            return false;
-        }
-        for (int i = 0; i < N; i++) {
-            if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public void setNoSims(boolean show, boolean simDetected) {
-        // Noop yay!
-    }
-
     @Override
     public void setEthernetIndicators(IconState state) {
-        boolean visible = state.visible && !mHideEthernet;
         int resId = state.icon;
         String description = state.contentDescription;
 
@@ -324,11 +227,6 @@
         }
     }
 
-    @Override
-    public void setMobileDataEnabled(boolean enabled) {
-        // Don't care.
-    }
-
     /**
      * Stores the statusbar state for no Calling & SMS.
      */
@@ -388,117 +286,4 @@
             return outStates;
         }
     }
-
-    private static abstract class SignalIconState {
-        public boolean visible;
-        public boolean activityOut;
-        public boolean activityIn;
-        public String slot;
-        public String contentDescription;
-
-        @Override
-        public boolean equals(Object o) {
-            // Skipping reference equality bc this should be more of a value type
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            SignalIconState that = (SignalIconState) o;
-            return visible == that.visible &&
-                    activityOut == that.activityOut &&
-                    activityIn == that.activityIn &&
-                    Objects.equals(contentDescription, that.contentDescription) &&
-                    Objects.equals(slot, that.slot);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(visible, activityOut, slot);
-        }
-
-        protected void copyTo(SignalIconState other) {
-            other.visible = visible;
-            other.activityIn = activityIn;
-            other.activityOut = activityOut;
-            other.slot = slot;
-            other.contentDescription = contentDescription;
-        }
-    }
-
-    /**
-     * A little different. This one delegates to SignalDrawable instead of a specific resId
-     */
-    public static class MobileIconState extends SignalIconState {
-        public int subId;
-        public int strengthId;
-        public int typeId;
-        public boolean showTriangle;
-        public boolean roaming;
-        public boolean needsLeadingPadding;
-        public CharSequence typeContentDescription;
-
-        private MobileIconState(int subId) {
-            super();
-            this.subId = subId;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            if (!super.equals(o)) {
-                return false;
-            }
-            MobileIconState that = (MobileIconState) o;
-            return subId == that.subId
-                    && strengthId == that.strengthId
-                    && typeId == that.typeId
-                    && showTriangle == that.showTriangle
-                    && roaming == that.roaming
-                    && needsLeadingPadding == that.needsLeadingPadding
-                    && Objects.equals(typeContentDescription, that.typeContentDescription);
-        }
-
-        @Override
-        public int hashCode() {
-
-            return Objects
-                    .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming,
-                            needsLeadingPadding, typeContentDescription);
-        }
-
-        public MobileIconState copy() {
-            MobileIconState copy = new MobileIconState(this.subId);
-            copyTo(copy);
-            return copy;
-        }
-
-        public void copyTo(MobileIconState other) {
-            super.copyTo(other);
-            other.subId = subId;
-            other.strengthId = strengthId;
-            other.typeId = typeId;
-            other.showTriangle = showTriangle;
-            other.roaming = roaming;
-            other.needsLeadingPadding = needsLeadingPadding;
-            other.typeContentDescription = typeContentDescription;
-        }
-
-        private static List<MobileIconState> copyStates(List<MobileIconState> inStates) {
-            ArrayList<MobileIconState> outStates = new ArrayList<>();
-            for (MobileIconState state : inStates) {
-                MobileIconState copy = new MobileIconState(state.subId);
-                state.copyTo(copy);
-                outStates.add(copy);
-            }
-
-            return outStates;
-        }
-
-        @Override public String toString() {
-            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId
-                    + ", showTriangle=" + showTriangle + ", roaming=" + roaming
-                    + ", typeId=" + typeId + ", visible=" + visible + ")";
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
new file mode 100644
index 0000000..fbc6b95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialogManagerExt.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.statusbar.phone
+
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+
+/** Whether dialogs are requesting for affordances to be hidden or not. */
+val SystemUIDialogManager.hideAffordancesRequest: Flow<Boolean>
+    get() = conflatedCallbackFlow {
+        val callback =
+            SystemUIDialogManager.Listener { hideAffordance ->
+                trySendWithFailureLogging(hideAffordance, "dialogHideAffordancesRequest")
+            }
+        registerListener(callback)
+        trySendWithFailureLogging(shouldHideAffordance(), "dialogHideAffordancesRequestInitial")
+        awaitClose { unregisterListener(callback) }
+    }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
index 29829e4..6e51ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt
@@ -18,8 +18,6 @@
 
 import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import javax.inject.Inject
 
 /** All flagging methods related to the new status bar pipeline (see b/238425913). */
@@ -28,30 +26,10 @@
 @Inject
 constructor(
     context: Context,
-    private val featureFlags: FeatureFlags,
 ) {
     private val mobileSlot = context.getString(com.android.internal.R.string.status_bar_mobile)
     private val wifiSlot = context.getString(com.android.internal.R.string.status_bar_wifi)
 
-    /** True if we should display the mobile icons using the new status bar data pipeline. */
-    fun useNewMobileIcons(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS)
-
-    /**
-     * True if we should run the new mobile icons backend to get the logging.
-     *
-     * Does *not* affect whether we render the mobile icons using the new backend data. See
-     * [useNewMobileIcons] for that.
-     */
-    fun runNewMobileIconsBackend(): Boolean =
-        featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS_BACKEND) || useNewMobileIcons()
-
-    /**
-     * Returns true if we should apply some coloring to the icons that were rendered with the new
-     * pipeline to help with debugging.
-     */
-    fun useDebugColoring(): Boolean =
-        featureFlags.isEnabled(Flags.NEW_STATUS_BAR_ICONS_DEBUG_COLORING)
-
     /**
      * For convenience in the StatusBarIconController, we want to gate some actions based on slot
      * name and the flag together.
@@ -59,5 +37,5 @@
      * @return true if this icon is controlled by any of the status bar pipeline flags
      */
     fun isIconControlledByFlags(slotName: String): Boolean =
-        slotName == wifiSlot || (slotName == mobileSlot && useNewMobileIcons())
+        slotName == wifiSlot || slotName == mobileSlot
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 27cc64f..0e99c67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -19,12 +19,10 @@
 import android.net.wifi.WifiManager
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.TableLogBufferFactory
-import com.android.systemui.log.LogBuffer
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,6 +44,8 @@
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinderImpl
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
@@ -58,9 +58,9 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
-import kotlinx.coroutines.flow.Flow
 import java.util.function.Supplier
 import javax.inject.Named
+import kotlinx.coroutines.flow.Flow
 
 @Module
 abstract class StatusBarPipelineModule {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
index a05ab84..d7fcf48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapter.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.phone.StatusBarIconController
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import java.io.PrintWriter
@@ -46,24 +45,18 @@
     val mobileIconsViewModel: MobileIconsViewModel,
     private val logger: MobileViewLogger,
     @Application private val scope: CoroutineScope,
-    private val statusBarPipelineFlags: StatusBarPipelineFlags,
 ) : CoreStartable {
     private var isCollecting: Boolean = false
     private var lastValue: List<Int>? = null
 
     override fun start() {
-        // Only notify the icon controller if we want to *render* the new icons.
-        // Note that this flow may still run if
-        // [statusBarPipelineFlags.runNewMobileIconsBackend] is true because we may want to
-        // get the logging data without rendering.
-        if (statusBarPipelineFlags.useNewMobileIcons()) {
-            scope.launch {
-                isCollecting = true
-                mobileIconsViewModel.subscriptionIdsFlow.collectLatest {
-                    logger.logUiAdapterSubIdsSentToIconController(it)
-                    lastValue = it
-                    iconController.setNewMobileIconSubIds(it)
-                }
+        // Start notifying the icon controller of subscriptions
+        scope.launch {
+            isCollecting = true
+            mobileIconsViewModel.subscriptionIdsFlow.collectLatest {
+                logger.logUiAdapterSubIdsSentToIconController(it)
+                lastValue = it
+                iconController.setNewMobileIconSubIds(it)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index a2a247a..c221109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -183,16 +183,10 @@
             }
 
             override fun onIconTintChanged(newTint: Int) {
-                if (viewModel.useDebugColoring) {
-                    return
-                }
                 iconTint.value = newTint
             }
 
             override fun onDecorTintChanged(newTint: Int) {
-                if (viewModel.useDebugColoring) {
-                    return
-                }
                 decorTint.value = newTint
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
index f775940..a51982c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Color
 import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
 
 /**
@@ -32,24 +31,14 @@
  */
 abstract class LocationBasedMobileViewModel(
     val commonImpl: MobileIconViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
-    debugTint: Int,
     val locationName: String,
     val verboseLogger: VerboseMobileViewLogger?,
 ) : MobileIconViewModelCommon by commonImpl {
-    val useDebugColoring: Boolean = statusBarPipelineFlags.useDebugColoring()
-
-    val defaultColor: Int =
-        if (useDebugColoring) {
-            debugTint
-        } else {
-            Color.WHITE
-        }
+    val defaultColor: Int = Color.WHITE
 
     companion object {
         fun viewModelForLocation(
             commonImpl: MobileIconViewModelCommon,
-            statusBarPipelineFlags: StatusBarPipelineFlags,
             verboseMobileViewLogger: VerboseMobileViewLogger,
             loc: StatusBarLocation,
         ): LocationBasedMobileViewModel =
@@ -57,39 +46,31 @@
                 StatusBarLocation.HOME ->
                     HomeMobileIconViewModel(
                         commonImpl,
-                        statusBarPipelineFlags,
                         verboseMobileViewLogger,
                     )
-                StatusBarLocation.KEYGUARD ->
-                    KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
-                StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+                StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl)
+                StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl)
             }
     }
 }
 
 class HomeMobileIconViewModel(
     commonImpl: MobileIconViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
     verboseMobileViewLogger: VerboseMobileViewLogger,
 ) :
     MobileIconViewModelCommon,
     LocationBasedMobileViewModel(
         commonImpl,
-        statusBarPipelineFlags,
-        debugTint = Color.CYAN,
         locationName = "Home",
         verboseMobileViewLogger,
     )
 
 class QsMobileIconViewModel(
     commonImpl: MobileIconViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
 ) :
     MobileIconViewModelCommon,
     LocationBasedMobileViewModel(
         commonImpl,
-        statusBarPipelineFlags,
-        debugTint = Color.GREEN,
         locationName = "QS",
         // Only do verbose logging for the Home location.
         verboseLogger = null,
@@ -97,13 +78,10 @@
 
 class KeyguardMobileIconViewModel(
     commonImpl: MobileIconViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
 ) :
     MobileIconViewModelCommon,
     LocationBasedMobileViewModel(
         commonImpl,
-        statusBarPipelineFlags,
-        debugTint = Color.MAGENTA,
         locationName = "Keyguard",
         // Only do verbose logging for the Home location.
         verboseLogger = null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 40b8c90..5cf887e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -98,7 +98,6 @@
         val common = commonViewModelForSub(subId)
         return LocationBasedMobileViewModel.viewModelForLocation(
             common,
-            statusBarPipelineFlags,
             verboseLogger,
             location,
         )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
index 6d71823..7a60d96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt
@@ -23,7 +23,6 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.statusbar.phone.StatusBarIconController
 import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
@@ -46,7 +45,6 @@
 constructor(
     private val iconController: StatusBarIconController,
     private val wifiViewModel: WifiViewModel,
-    private val statusBarPipelineFlags: StatusBarPipelineFlags,
 ) {
     /**
      * Binds the container for all the status bar icons to a view model, so that we inflate the wifi
@@ -60,8 +58,7 @@
         statusBarIconGroup: ViewGroup,
         location: StatusBarLocation,
     ): LocationBasedWifiViewModel {
-        val locationViewModel =
-            viewModelForLocation(wifiViewModel, statusBarPipelineFlags, location)
+        val locationViewModel = viewModelForLocation(wifiViewModel, location)
 
         statusBarIconGroup.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index e819c4f..3082a66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -157,16 +157,10 @@
             }
 
             override fun onIconTintChanged(newTint: Int) {
-                if (viewModel.useDebugColoring) {
-                    return
-                }
                 iconTint.value = newTint
             }
 
             override fun onDecorTintChanged(newTint: Int) {
-                if (viewModel.useDebugColoring) {
-                    return
-                }
                 decorTint.value = newTint
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
index b731a41..cd5b92c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt
@@ -18,7 +18,6 @@
 
 import android.graphics.Color
 import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 
 /**
  * A view model for a wifi icon in a specific location. This allows us to control parameters that
@@ -27,18 +26,9 @@
  * Must be subclassed for each distinct location.
  */
 abstract class LocationBasedWifiViewModel(
-    val commonImpl: WifiViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
-    debugTint: Int,
+    private val commonImpl: WifiViewModelCommon,
 ) : WifiViewModelCommon by commonImpl {
-    val useDebugColoring: Boolean = statusBarPipelineFlags.useDebugColoring()
-
-    val defaultColor: Int =
-        if (useDebugColoring) {
-            debugTint
-        } else {
-            Color.WHITE
-        }
+    val defaultColor: Int = Color.WHITE
 
     companion object {
         /**
@@ -47,13 +37,12 @@
          */
         fun viewModelForLocation(
             commonImpl: WifiViewModelCommon,
-            flags: StatusBarPipelineFlags,
             location: StatusBarLocation,
         ): LocationBasedWifiViewModel =
             when (location) {
-                StatusBarLocation.HOME -> HomeWifiViewModel(commonImpl, flags)
-                StatusBarLocation.KEYGUARD -> KeyguardWifiViewModel(commonImpl, flags)
-                StatusBarLocation.QS -> QsWifiViewModel(commonImpl, flags)
+                StatusBarLocation.HOME -> HomeWifiViewModel(commonImpl)
+                StatusBarLocation.KEYGUARD -> KeyguardWifiViewModel(commonImpl)
+                StatusBarLocation.QS -> QsWifiViewModel(commonImpl)
             }
     }
 }
@@ -64,23 +53,14 @@
  */
 class HomeWifiViewModel(
     commonImpl: WifiViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
-) :
-    WifiViewModelCommon,
-    LocationBasedWifiViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.CYAN)
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
 
 /** A view model for the wifi icon shown on keyguard (lockscreen). */
 class KeyguardWifiViewModel(
     commonImpl: WifiViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
-) :
-    WifiViewModelCommon,
-    LocationBasedWifiViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.MAGENTA)
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
 
 /** A view model for the wifi icon shown in quick settings (when the shade is pulled down). */
 class QsWifiViewModel(
     commonImpl: WifiViewModelCommon,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
-) :
-    WifiViewModelCommon,
-    LocationBasedWifiViewModel(commonImpl, statusBarPipelineFlags, debugTint = Color.GREEN)
+) : WifiViewModelCommon, LocationBasedWifiViewModel(commonImpl)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS
deleted file mode 100644
index a601e9b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/OWNERS
+++ /dev/null
@@ -1,8 +0,0 @@
-# Android TV Core Framework
-rgl@google.com
-valiiftime@google.com
-galinap@google.com
-patrikf@google.com
-robhor@google.com
-sergeynv@google.com
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
deleted file mode 100644
index d35d340..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.systemui.CoreStartable;
-import com.android.systemui.assist.AssistManager;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyboardShortcuts;
-
-import dagger.Lazy;
-
-import javax.inject.Inject;
-
-/**
- * Status bar implementation for "large screen" products that mostly present no on-screen nav.
- * Serves as a collection of UI components, rather than showing its own UI.
- */
-@SysUISingleton
-public class TvStatusBar implements CoreStartable, CommandQueue.Callbacks {
-
-    private static final String ACTION_SHOW_PIP_MENU =
-            "com.android.wm.shell.pip.tv.notification.action.SHOW_PIP_MENU";
-    private static final String SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF";
-
-    private final Context mContext;
-    private final CommandQueue mCommandQueue;
-    private final Lazy<AssistManager> mAssistManagerLazy;
-
-    @Inject
-    public TvStatusBar(Context context, CommandQueue commandQueue,
-            Lazy<AssistManager> assistManagerLazy) {
-        mContext = context;
-        mCommandQueue = commandQueue;
-        mAssistManagerLazy = assistManagerLazy;
-    }
-
-    @Override
-    public void start() {
-        final IStatusBarService barService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
-        mCommandQueue.addCallback(this);
-        try {
-            barService.registerStatusBar(mCommandQueue);
-        } catch (RemoteException ex) {
-            // If the system process isn't there we're doomed anyway.
-        }
-    }
-
-    @Override
-    public void startAssist(Bundle args) {
-        mAssistManagerLazy.get().startAssist(args);
-    }
-
-    @Override
-    public void showPictureInPictureMenu() {
-        mContext.sendBroadcast(
-                new Intent(ACTION_SHOW_PIP_MENU).setPackage(mContext.getPackageName()),
-                SYSTEMUI_PERMISSION);
-    }
-
-    @Override
-    public void toggleKeyboardShortcutsMenu(int deviceId) {
-        KeyboardShortcuts.show(mContext, deviceId);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
deleted file mode 100644
index b938c90..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-@file:JvmName("VpnStatusObserver")
-
-package com.android.systemui.statusbar.tv
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.content.Context
-import com.android.internal.messages.nano.SystemMessageProto
-import com.android.internal.net.VpnConfig
-import com.android.systemui.CoreStartable
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.policy.SecurityController
-import javax.inject.Inject
-
-/**
- * Observes if a vpn connection is active and displays a notification to the user
- */
-@SysUISingleton
-class VpnStatusObserver @Inject constructor(
-    private val context: Context,
-    private val securityController: SecurityController
-) : CoreStartable,
-        SecurityController.SecurityControllerCallback {
-
-    private var vpnConnected = false
-    private val notificationManager = NotificationManager.from(context)
-    private val notificationChannel = createNotificationChannel()
-    private val vpnConnectedNotificationBuilder = createVpnConnectedNotificationBuilder()
-    private val vpnDisconnectedNotification = createVpnDisconnectedNotification()
-
-    private val vpnIconId: Int
-        get() = if (securityController.isVpnBranded) {
-            R.drawable.stat_sys_branded_vpn
-        } else {
-            R.drawable.stat_sys_vpn_ic
-        }
-
-    private val vpnName: String?
-        get() = securityController.primaryVpnName ?: securityController.workProfileVpnName
-
-    override fun start() {
-        // register callback to vpn state changes
-        securityController.addCallback(this)
-    }
-
-    override fun onStateChanged() {
-        securityController.isVpnEnabled.let { newVpnConnected ->
-            if (vpnConnected != newVpnConnected) {
-                if (newVpnConnected) {
-                    notifyVpnConnected()
-                } else {
-                    notifyVpnDisconnected()
-                }
-                vpnConnected = newVpnConnected
-            }
-        }
-    }
-
-    private fun notifyVpnConnected() = notificationManager.notify(
-            NOTIFICATION_TAG,
-            SystemMessageProto.SystemMessage.NOTE_VPN_STATUS,
-            createVpnConnectedNotification()
-    )
-
-    private fun notifyVpnDisconnected() = notificationManager.run {
-        // remove existing connected notification
-        cancel(NOTIFICATION_TAG, SystemMessageProto.SystemMessage.NOTE_VPN_STATUS)
-        // show the disconnected notification only for a short while
-        notify(NOTIFICATION_TAG, SystemMessageProto.SystemMessage.NOTE_VPN_DISCONNECTED,
-                vpnDisconnectedNotification)
-    }
-
-    private fun createNotificationChannel() =
-            NotificationChannel(
-                    NOTIFICATION_CHANNEL_TV_VPN,
-                    NOTIFICATION_CHANNEL_TV_VPN,
-                    NotificationManager.IMPORTANCE_HIGH
-            ).also {
-                notificationManager.createNotificationChannel(it)
-            }
-
-    private fun createVpnConnectedNotification() =
-            vpnConnectedNotificationBuilder
-                    .apply {
-                        vpnName?.let {
-                            setContentText(
-                                    context.getString(
-                                            R.string.notification_disclosure_vpn_text, it
-                                    )
-                            )
-                        }
-                    }
-                    .build()
-
-    private fun createVpnConnectedNotificationBuilder() =
-            Notification.Builder(context, NOTIFICATION_CHANNEL_TV_VPN)
-                    .setSmallIcon(vpnIconId)
-                    .setVisibility(Notification.VISIBILITY_PUBLIC)
-                    .setCategory(Notification.CATEGORY_SYSTEM)
-                    .extend(Notification.TvExtender())
-                    .setOngoing(true)
-                    .setContentTitle(context.getString(R.string.notification_vpn_connected))
-                    .setContentIntent(VpnConfig.getIntentForStatusPanel(context))
-
-    private fun createVpnDisconnectedNotification() =
-            Notification.Builder(context, NOTIFICATION_CHANNEL_TV_VPN)
-                    .setSmallIcon(vpnIconId)
-                    .setVisibility(Notification.VISIBILITY_PUBLIC)
-                    .setCategory(Notification.CATEGORY_SYSTEM)
-                    .extend(Notification.TvExtender())
-                    .setTimeoutAfter(VPN_DISCONNECTED_NOTIFICATION_TIMEOUT_MS)
-                    .setContentTitle(context.getString(R.string.notification_vpn_disconnected))
-                    .build()
-
-    companion object {
-        const val NOTIFICATION_CHANNEL_TV_VPN = "VPN Status"
-        val NOTIFICATION_TAG: String = VpnStatusObserver::class.java.simpleName
-
-        private const val TAG = "TvVpnNotification"
-        private const val VPN_DISCONNECTED_NOTIFICATION_TIMEOUT_MS = 5_000L
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
deleted file mode 100644
index fd7c30f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationAdapter.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.notifications;
-
-import android.app.BroadcastOptions;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.systemui.R;
-
-/**
- * Adapter for the VerticalGridView of the TvNotificationsPanelView.
- */
-public class TvNotificationAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
-    private static final String TAG = "TvNotificationAdapter";
-    private SparseArray<StatusBarNotification> mNotifications;
-
-    public TvNotificationAdapter() {
-        setHasStableIds(true);
-    }
-
-    @NonNull
-    @Override
-    public TvNotificationViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.tv_notification_item,
-                parent, false);
-        return new TvNotificationViewHolder(view);
-    }
-
-    @Override
-    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
-        if (mNotifications == null) {
-            Log.e(TAG, "Could not bind view holder because the notification is missing");
-            return;
-        }
-
-        TvNotificationViewHolder holder = (TvNotificationViewHolder) viewHolder;
-        Notification notification = mNotifications.valueAt(position).getNotification();
-        holder.mTitle.setText(notification.extras.getString(Notification.EXTRA_TITLE));
-        holder.mDetails.setText(notification.extras.getString(Notification.EXTRA_TEXT));
-        holder.mPendingIntent = notification.contentIntent;
-    }
-
-    @Override
-    public int getItemCount() {
-        return mNotifications == null ? 0 : mNotifications.size();
-    }
-
-    @Override
-    public long getItemId(int position) {
-        // the item id is the notification id
-        return mNotifications.keyAt(position);
-    }
-
-    /**
-     * Updates the notifications and calls notifyDataSetChanged().
-     */
-    public void setNotifications(SparseArray<StatusBarNotification> notifications) {
-        this.mNotifications = notifications;
-        notifyDataSetChanged();
-    }
-
-    private static class TvNotificationViewHolder extends RecyclerView.ViewHolder implements
-            View.OnClickListener {
-        final TextView mTitle;
-        final TextView mDetails;
-        PendingIntent mPendingIntent;
-
-        protected TvNotificationViewHolder(View itemView) {
-            super(itemView);
-            mTitle = itemView.findViewById(R.id.tv_notification_title);
-            mDetails = itemView.findViewById(R.id.tv_notification_details);
-            itemView.setOnClickListener(this);
-        }
-
-        @Override
-        public void onClick(View v) {
-            try {
-                if (mPendingIntent != null) {
-                    BroadcastOptions options = BroadcastOptions.makeBasic();
-                    options.setInteractive(true);
-                    options.setPendingIntentBackgroundActivityStartMode(
-                            BroadcastOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-                    mPendingIntent.send(options.toBundle());
-                }
-            } catch (PendingIntent.CanceledException e) {
-                Log.d(TAG, "Pending intent canceled for : " + mPendingIntent);
-            }
-        }
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
deleted file mode 100644
index b92725b..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationHandler.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.notifications;
-
-import android.annotation.Nullable;
-import android.app.Notification;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.NotificationListener;
-
-import javax.inject.Inject;
-
-/**
- * Keeps track of the notifications on TV.
- */
-@SysUISingleton
-public class TvNotificationHandler implements CoreStartable,
-        NotificationListener.NotificationHandler {
-    private static final String TAG = "TvNotificationHandler";
-    private final NotificationListener mNotificationListener;
-    private final SparseArray<StatusBarNotification> mNotifications = new SparseArray<>();
-    @Nullable
-    private Listener mUpdateListener;
-
-    @Inject
-    public TvNotificationHandler(NotificationListener notificationListener) {
-        mNotificationListener = notificationListener;
-    }
-
-    public SparseArray<StatusBarNotification> getCurrentNotifications() {
-        return mNotifications;
-    }
-
-    public void setTvNotificationListener(Listener listener) {
-        mUpdateListener = listener;
-    }
-
-    @Override
-    public void start() {
-        mNotificationListener.addNotificationHandler(this);
-        mNotificationListener.registerAsSystemService();
-    }
-
-    @Override
-    public void onNotificationPosted(StatusBarNotification sbn,
-            NotificationListenerService.RankingMap rankingMap) {
-        if (!new Notification.TvExtender(sbn.getNotification()).isAvailableOnTv()) {
-            Log.v(TAG, "Notification not added because it isn't relevant for tv");
-            return;
-        }
-
-        mNotifications.put(sbn.getId(), sbn);
-        if (mUpdateListener != null) {
-            mUpdateListener.notificationsUpdated(mNotifications);
-        }
-        Log.d(TAG, "Notification added");
-    }
-
-    @Override
-    public void onNotificationRemoved(StatusBarNotification sbn,
-            NotificationListenerService.RankingMap rankingMap) {
-
-        if (mNotifications.contains(sbn.getId())) {
-            mNotifications.remove(sbn.getId());
-            Log.d(TAG, "Notification removed");
-
-            if (mUpdateListener != null) {
-                mUpdateListener.notificationsUpdated(mNotifications);
-            }
-        }
-    }
-
-    @Override
-    public void onNotificationRemoved(StatusBarNotification sbn,
-            NotificationListenerService.RankingMap rankingMap, int reason) {
-        onNotificationRemoved(sbn, rankingMap);
-    }
-
-    @Override
-    public void onNotificationRankingUpdate(NotificationListenerService.RankingMap rankingMap) {
-        // noop
-    }
-
-    @Override
-    public void onNotificationsInitialized() {
-        // noop
-    }
-
-    /**
-     * Get notified when the notifications are updated.
-     */
-    interface Listener {
-        void notificationsUpdated(SparseArray<StatusBarNotification> sbns);
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
deleted file mode 100644
index dbbd0b8..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.notifications;
-
-import android.Manifest;
-import android.app.NotificationManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.UserHandle;
-import android.util.Log;
-
-import com.android.systemui.CoreStartable;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.statusbar.CommandQueue;
-
-import javax.inject.Inject;
-
-/**
- * Offers control methods for the notification panel handler on TV devices.
- */
-@SysUISingleton
-public class TvNotificationPanel implements CoreStartable, CommandQueue.Callbacks {
-    private static final String TAG = "TvNotificationPanel";
-    private final Context mContext;
-    private final CommandQueue mCommandQueue;
-    private final String mNotificationHandlerPackage;
-
-    @Inject
-    public TvNotificationPanel(Context context, CommandQueue commandQueue) {
-        mContext = context;
-        mCommandQueue = commandQueue;
-        mNotificationHandlerPackage = mContext.getResources().getString(
-                com.android.internal.R.string.config_notificationHandlerPackage);
-    }
-
-    @Override
-    public void start() {
-        mCommandQueue.addCallback(this);
-    }
-
-    @Override
-    public void togglePanel() {
-        if (!mNotificationHandlerPackage.isEmpty()) {
-            startNotificationHandlerActivity(
-                    new Intent(NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL));
-        } else {
-            openInternalNotificationPanel(
-                    NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL);
-        }
-    }
-
-    @Override
-    public void animateExpandNotificationsPanel() {
-        if (!mNotificationHandlerPackage.isEmpty()) {
-            startNotificationHandlerActivity(
-                    new Intent(NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL));
-        } else {
-            openInternalNotificationPanel(
-                    NotificationManager.ACTION_OPEN_NOTIFICATION_HANDLER_PANEL);
-        }
-    }
-
-    @Override
-    public void animateCollapsePanels(int flags, boolean force) {
-        if (!mNotificationHandlerPackage.isEmpty()
-                && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) {
-            Intent closeNotificationIntent = new Intent(
-                    NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
-            closeNotificationIntent.setPackage(mNotificationHandlerPackage);
-            mContext.sendBroadcastAsUser(closeNotificationIntent, UserHandle.CURRENT);
-        } else {
-            openInternalNotificationPanel(
-                    NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL);
-        }
-    }
-
-    private void openInternalNotificationPanel(String action) {
-        Intent intent = new Intent(mContext, TvNotificationPanelActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.setAction(action);
-        mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
-    }
-
-    /**
-     * Starts the activity intent if all of the following are true
-     * <ul>
-     * <li> the notification handler package is a system component </li>
-     * <li> the provided intent is handled by the notification handler package </li>
-     * <li> the notification handler requests the
-     * {@link android.Manifest.permission#STATUS_BAR_SERVICE} permission for the given intent</li>
-     * </ul>
-     *
-     * @param intent The intent for starting the desired activity
-     */
-    private void startNotificationHandlerActivity(Intent intent) {
-        intent.setPackage(mNotificationHandlerPackage);
-        PackageManager pm = mContext.getPackageManager();
-        ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_SYSTEM_ONLY);
-        if (ri != null && ri.activityInfo != null) {
-            if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals(
-                    Manifest.permission.STATUS_BAR_SERVICE)) {
-                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
-                mContext.startActivityAsUser(intent, UserHandle.CURRENT);
-            } else {
-                Log.e(TAG,
-                        "Not launching notification handler activity: Notification handler does "
-                                + "not require the STATUS_BAR_SERVICE permission for intent "
-                                + intent.getAction());
-            }
-        } else {
-            Log.e(TAG,
-                    "Not launching notification handler activity: Could not resolve activityInfo "
-                            + "for intent "
-                            + intent.getAction());
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
deleted file mode 100644
index b325b10..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanelActivity.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.tv.notifications;
-
-import android.annotation.NonNull;
-import android.app.Activity;
-import android.app.NotificationManager;
-import android.content.Intent;
-import android.graphics.drawable.ColorDrawable;
-import android.os.Bundle;
-import android.service.notification.StatusBarNotification;
-import android.util.SparseArray;
-import android.view.Gravity;
-import android.view.View;
-
-import androidx.leanback.widget.VerticalGridView;
-
-import com.android.systemui.R;
-
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * This Activity shows a notification panel for tv. It is used if no other app (e.g. a launcher) can
- * be found to show the notifications.
- */
-public class TvNotificationPanelActivity extends Activity implements
-        TvNotificationHandler.Listener {
-    private final TvNotificationHandler mTvNotificationHandler;
-    private TvNotificationAdapter mTvNotificationAdapter;
-    private VerticalGridView mNotificationListView;
-    private View mNotificationPlaceholder;
-    private boolean mPanelAlreadyOpen = false;
-    private final Consumer<Boolean> mBlurConsumer = this::enableBlur;
-
-    @Inject
-    public TvNotificationPanelActivity(TvNotificationHandler tvNotificationHandler) {
-        super();
-        mTvNotificationHandler = tvNotificationHandler;
-    }
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        if (maybeClosePanel(getIntent())) {
-            return;
-        }
-        mPanelAlreadyOpen = true;
-
-        setContentView(R.layout.tv_notification_panel);
-
-        mNotificationPlaceholder = findViewById(R.id.no_tv_notifications);
-        mTvNotificationAdapter = new TvNotificationAdapter();
-
-        mNotificationListView = findViewById(R.id.notifications_list);
-        mNotificationListView.setAdapter(mTvNotificationAdapter);
-        mNotificationListView.setColumnWidth(R.dimen.tv_notification_panel_width);
-
-        mTvNotificationHandler.setTvNotificationListener(this);
-        notificationsUpdated(mTvNotificationHandler.getCurrentNotifications());
-    }
-
-    @Override
-    public void notificationsUpdated(@NonNull SparseArray<StatusBarNotification> notificationList) {
-        mTvNotificationAdapter.setNotifications(notificationList);
-
-        boolean noNotifications = notificationList.size() == 0;
-        mNotificationListView.setVisibility(noNotifications ? View.GONE : View.VISIBLE);
-        mNotificationPlaceholder.setVisibility(noNotifications ? View.VISIBLE : View.GONE);
-    }
-
-    @Override
-    public void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        maybeClosePanel(intent);
-    }
-
-    /**
-     * Handles intents from onCreate and onNewIntent.
-     *
-     * @return true if the panel is being closed, false if it is being opened
-     */
-    private boolean maybeClosePanel(Intent intent) {
-        if (NotificationManager.ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL.equals(intent.getAction())
-                || (mPanelAlreadyOpen
-                && NotificationManager.ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL.equals(
-                intent.getAction()))) {
-            finish();
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        getWindow().setGravity(Gravity.END);
-        getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
-    }
-
-    private void enableBlur(boolean enabled) {
-        if (enabled) {
-            int blurRadius = getResources().getDimensionPixelSize(
-                    R.dimen.tv_notification_blur_radius);
-            getWindow().setBackgroundDrawable(
-                    new ColorDrawable(getColor(R.color.tv_notification_blur_background_color)));
-            getWindow().setBackgroundBlurRadius(blurRadius);
-        } else {
-            getWindow().setBackgroundDrawable(
-                    new ColorDrawable(getColor(R.color.tv_notification_default_background_color)));
-            getWindow().setBackgroundBlurRadius(0);
-        }
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-        mTvNotificationHandler.setTvNotificationListener(null);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 7ed56e7..7aeba66 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -88,7 +88,7 @@
     private val chipbarAnimator: ChipbarAnimator,
     private val falsingManager: FalsingManager,
     private val falsingCollector: FalsingCollector,
-    private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler?,
+    private val swipeChipbarAwayGestureHandler: SwipeChipbarAwayGestureHandler,
     private val viewUtil: ViewUtil,
     private val vibratorHelper: VibratorHelper,
     wakeLockBuilder: WakeLock.Builder,
@@ -289,10 +289,6 @@
     }
 
     private fun updateGestureListening() {
-        if (swipeChipbarAwayGestureHandler == null) {
-            return
-        }
-
         val currentDisplayInfo = getCurrentDisplayInfo()
         if (currentDisplayInfo != null && currentDisplayInfo.info.allowSwipeToDismiss) {
             swipeChipbarAwayGestureHandler.setViewFetcher { currentDisplayInfo.view }
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
index 9dbc4b3..80de523 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandler.kt
@@ -19,10 +19,12 @@
 import android.content.Context
 import android.view.MotionEvent
 import android.view.View
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.settings.DisplayTracker
 import com.android.systemui.statusbar.gesture.SwipeUpGestureHandler
 import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
 import com.android.systemui.util.boundsOnScreen
+import javax.inject.Inject
 
 /**
  * A class to detect when a user has swiped the chipbar away.
@@ -30,7 +32,10 @@
  * Effectively [SysUISingleton]. But, this shouldn't be created if the gesture isn't enabled. See
  * [TemporaryDisplayModule.provideSwipeChipbarAwayGestureHandler].
  */
-class SwipeChipbarAwayGestureHandler(
+@SysUISingleton
+class SwipeChipbarAwayGestureHandler
+@Inject
+constructor(
     context: Context,
     displayTracker: DisplayTracker,
     logger: SwipeUpGestureLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
index cae1308..2d05573 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/dagger/TemporaryDisplayModule.kt
@@ -16,14 +16,9 @@
 
 package com.android.systemui.temporarydisplay.dagger
 
-import android.content.Context
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.media.taptotransfer.MediaTttFlags
-import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.gesture.SwipeUpGestureLogger
-import com.android.systemui.temporarydisplay.chipbar.SwipeChipbarAwayGestureHandler
 import dagger.Module
 import dagger.Provides
 
@@ -36,20 +31,5 @@
         fun provideChipbarLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create("ChipbarLog", 40)
         }
-
-        @Provides
-        @SysUISingleton
-        fun provideSwipeChipbarAwayGestureHandler(
-            mediaTttFlags: MediaTttFlags,
-            context: Context,
-            displayTracker: DisplayTracker,
-            logger: SwipeUpGestureLogger,
-        ): SwipeChipbarAwayGestureHandler? {
-            return if (mediaTttFlags.isMediaTttDismissGestureEnabled()) {
-                SwipeChipbarAwayGestureHandler(context, displayTracker, logger)
-            } else {
-                null
-            }
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 5d09e06..a501e87 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -20,6 +20,11 @@
 
 import com.android.systemui.Dependency;
 
+/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ */
+@Deprecated
 public abstract class TunerService {
 
     public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8cfe2ea..ccc0a79 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -56,7 +56,11 @@
 
 
 /**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ * This class will interact with SecureSettings using the main looper.
  */
+@Deprecated
 @SysUISingleton
 public class TunerServiceImpl extends TunerService {
 
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
deleted file mode 100644
index 573fbf7..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2021 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.tv
-
-import com.android.systemui.CoreStartable
-import com.android.systemui.SliceBroadcastRelayHandler
-import com.android.systemui.accessibility.WindowMagnification
-import com.android.systemui.dagger.qualifiers.PerUser
-import com.android.systemui.globalactions.GlobalActionsComponent
-import com.android.systemui.keyboard.KeyboardUI
-import com.android.systemui.media.RingtonePlayer
-import com.android.systemui.media.systemsounds.HomeSoundEffectController
-import com.android.systemui.power.PowerUI
-import com.android.systemui.privacy.television.TvPrivacyChipsController
-import com.android.systemui.shortcut.ShortcutKeyDispatcher
-import com.android.systemui.statusbar.notification.InstantAppNotifier
-import com.android.systemui.statusbar.tv.TvStatusBar
-import com.android.systemui.statusbar.tv.VpnStatusObserver
-import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler
-import com.android.systemui.statusbar.tv.notifications.TvNotificationPanel
-import com.android.systemui.theme.ThemeOverlayController
-import com.android.systemui.toast.ToastUI
-import com.android.systemui.usb.StorageNotification
-import com.android.systemui.util.NotificationChannels
-import com.android.systemui.volume.VolumeUI
-import com.android.systemui.wmshell.WMShell
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
-
-/**
- * Collection of {@link CoreStartable}s that should be run on TV.
- */
-@Module
-abstract class TVSystemUICoreStartableModule {
-    /** Inject into GlobalActionsComponent.  */
-    @Binds
-    @IntoMap
-    @ClassKey(GlobalActionsComponent::class)
-    abstract fun bindGlobalActionsComponent(sysui: GlobalActionsComponent): CoreStartable
-
-    /** Inject into HomeSoundEffectController.  */
-    @Binds
-    @IntoMap
-    @ClassKey(HomeSoundEffectController::class)
-    abstract fun bindHomeSoundEffectController(sysui: HomeSoundEffectController): CoreStartable
-
-    /** Inject into InstantAppNotifier.  */
-    @Binds
-    @IntoMap
-    @ClassKey(InstantAppNotifier::class)
-    abstract fun bindInstantAppNotifier(sysui: InstantAppNotifier): CoreStartable
-
-    /** Inject into KeyboardUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(KeyboardUI::class)
-    abstract fun bindKeyboardUI(sysui: KeyboardUI): CoreStartable
-
-    /** Inject into NotificationChannels.  */
-    @Binds
-    @IntoMap
-    @ClassKey(NotificationChannels::class)
-    @PerUser
-    abstract fun bindNotificationChannels(sysui: NotificationChannels): CoreStartable
-
-    /** Inject into PowerUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(PowerUI::class)
-    abstract fun bindPowerUI(sysui: PowerUI): CoreStartable
-
-    /** Inject into RingtonePlayer.  */
-    @Binds
-    @IntoMap
-    @ClassKey(RingtonePlayer::class)
-    abstract fun bind(sysui: RingtonePlayer): CoreStartable
-
-    /** Inject into ShortcutKeyDispatcher.  */
-    @Binds
-    @IntoMap
-    @ClassKey(ShortcutKeyDispatcher::class)
-    abstract fun bindShortcutKeyDispatcher(sysui: ShortcutKeyDispatcher): CoreStartable
-
-    /** Inject into SliceBroadcastRelayHandler.  */
-    @Binds
-    @IntoMap
-    @ClassKey(SliceBroadcastRelayHandler::class)
-    abstract fun bindSliceBroadcastRelayHandler(sysui: SliceBroadcastRelayHandler): CoreStartable
-
-    /** Inject into StorageNotification.  */
-    @Binds
-    @IntoMap
-    @ClassKey(StorageNotification::class)
-    abstract fun bindStorageNotification(sysui: StorageNotification): CoreStartable
-
-    /** Inject into ThemeOverlayController.  */
-    @Binds
-    @IntoMap
-    @ClassKey(ThemeOverlayController::class)
-    abstract fun bindThemeOverlayController(sysui: ThemeOverlayController): CoreStartable
-
-    /** Inject into ToastUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(ToastUI::class)
-    abstract fun bindToastUI(service: ToastUI): CoreStartable
-
-    /** Inject into TvNotificationHandler.  */
-    @Binds
-    @IntoMap
-    @ClassKey(TvNotificationHandler::class)
-    abstract fun bindTvNotificationHandler(sysui: TvNotificationHandler): CoreStartable
-
-    /** Inject into TvNotificationPanel.  */
-    @Binds
-    @IntoMap
-    @ClassKey(TvNotificationPanel::class)
-    abstract fun bindTvNotificationPanel(sysui: TvNotificationPanel): CoreStartable
-
-    /** Inject into TvPrivacyChipsController.  */
-    @Binds
-    @IntoMap
-    @ClassKey(TvPrivacyChipsController::class)
-    abstract fun bindTvPrivacyChipsController(sysui: TvPrivacyChipsController): CoreStartable
-
-    /** Inject into TvStatusBar.  */
-    @Binds
-    @IntoMap
-    @ClassKey(TvStatusBar::class)
-    abstract fun bindTvStatusBar(sysui: TvStatusBar): CoreStartable
-
-    /** Inject into VolumeUI.  */
-    @Binds
-    @IntoMap
-    @ClassKey(VolumeUI::class)
-    abstract fun bindVolumeUI(sysui: VolumeUI): CoreStartable
-
-    /** Inject into VpnStatusObserver.  */
-    @Binds
-    @IntoMap
-    @ClassKey(VpnStatusObserver::class)
-    abstract fun bindVpnStatusObserver(sysui: VpnStatusObserver): CoreStartable
-
-    /** Inject into WindowMagnification.  */
-    @Binds
-    @IntoMap
-    @ClassKey(WindowMagnification::class)
-    abstract fun bindWindowMagnification(sysui: WindowMagnification): CoreStartable
-
-    /** Inject into WMShell.  */
-    @Binds
-    @IntoMap
-    @ClassKey(WMShell::class)
-    abstract fun bindWMShell(sysui: WMShell): CoreStartable
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java b/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
deleted file mode 100644
index 90f2434..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvBottomSheetActivity.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2021 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.tv;
-
-import android.app.Activity;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.systemui.R;
-
-import java.util.Collections;
-import java.util.function.Consumer;
-
-/**
- * Generic bottom sheet with up to two icons in the beginning and two buttons.
- */
-public abstract class TvBottomSheetActivity extends Activity {
-
-    private static final String TAG = TvBottomSheetActivity.class.getSimpleName();
-    private Drawable mBackgroundWithBlur;
-    private Drawable mBackgroundWithoutBlur;
-
-    private final Consumer<Boolean> mBlurConsumer = this::onBlurChanged;
-
-    private void onBlurChanged(boolean enabled) {
-        Log.v(TAG, "blur enabled: " + enabled);
-        getWindow().setBackgroundDrawable(enabled ? mBackgroundWithBlur : mBackgroundWithoutBlur);
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.tv_bottom_sheet);
-
-        overridePendingTransition(R.anim.tv_bottom_sheet_enter, 0);
-
-        mBackgroundWithBlur = getResources()
-                .getDrawable(R.drawable.bottom_sheet_background_with_blur);
-        mBackgroundWithoutBlur = getResources().getDrawable(R.drawable.bottom_sheet_background);
-
-        DisplayMetrics metrics = getResources().getDisplayMetrics();
-        int screenWidth = metrics.widthPixels;
-        int screenHeight = metrics.heightPixels;
-        int marginPx = getResources().getDimensionPixelSize(R.dimen.bottom_sheet_margin);
-
-        WindowManager.LayoutParams windowParams = getWindow().getAttributes();
-        windowParams.width = screenWidth - marginPx * 2;
-        windowParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
-        windowParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
-        windowParams.horizontalMargin = 0f;
-        windowParams.verticalMargin = (float) marginPx / screenHeight;
-        windowParams.format = PixelFormat.TRANSPARENT;
-        windowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-        windowParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-        windowParams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-        getWindow().setAttributes(windowParams);
-        getWindow().setElevation(getWindow().getElevation() + 5);
-        getWindow().setBackgroundBlurRadius(getResources().getDimensionPixelSize(
-                R.dimen.bottom_sheet_background_blur_radius));
-
-        final View rootView = findViewById(R.id.bottom_sheet);
-        rootView.addOnLayoutChangeListener((view, l, t, r, b, oldL, oldT, oldR, oldB) -> {
-            rootView.setUnrestrictedPreferKeepClearRects(
-                    Collections.singletonList(new Rect(0, 0, r - l, b - t)));
-        });
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        getWindowManager().addCrossWindowBlurEnabledListener(mBlurConsumer);
-    }
-
-    @Override
-    public void onDetachedFromWindow() {
-        getWindowManager().removeCrossWindowBlurEnabledListener(mBlurConsumer);
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    public void finish() {
-        super.finish();
-        overridePendingTransition(0, R.anim.tv_bottom_sheet_exit);
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
deleted file mode 100644
index 117cba7..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import com.android.systemui.dagger.GlobalModule;
-import com.android.systemui.dagger.GlobalRootComponent;
-
-import javax.inject.Singleton;
-
-import dagger.Component;
-
-/**
- * Root component for Dagger injection.
- */
-@Singleton
-@Component(modules = {GlobalModule.class})
-public interface TvGlobalRootComponent extends GlobalRootComponent {
-    /**
-     * Component Builder interface. This allows to bind Context instance in the component
-     */
-    @Component.Builder
-    interface Builder extends GlobalRootComponent.Builder {
-        TvGlobalRootComponent build();
-    }
-
-    @Override
-    TvWMComponent.Builder getWMComponentBuilder();
-
-    @Override
-    TvSysUIComponent.Builder getSysUIComponent();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
deleted file mode 100644
index 82589d3..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import com.android.systemui.dagger.DefaultComponentBinder;
-import com.android.systemui.dagger.DependencyProvider;
-import com.android.systemui.dagger.SysUIComponent;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.SystemUIModule;
-import com.android.systemui.globalactions.ShutdownUiModule;
-import com.android.systemui.keyguard.dagger.KeyguardModule;
-import com.android.systemui.recents.RecentsModule;
-import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.wallpapers.dagger.NoopWallpaperModule;
-
-import dagger.Subcomponent;
-
-/**
- * Dagger Subcomponent for Core SysUI.
- */
-@SysUISingleton
-@Subcomponent(modules = {
-        CentralSurfacesDependenciesModule.class,
-        DefaultComponentBinder.class,
-        DependencyProvider.class,
-        KeyguardModule.class,
-        NoopWallpaperModule.class,
-        NotificationRowModule.class,
-        NotificationsModule.class,
-        RecentsModule.class,
-        ShutdownUiModule.class,
-        SystemUIModule.class,
-        TvSystemUIBinder.class,
-        TVSystemUICoreStartableModule.class,
-        TvSystemUIModule.class})
-public interface TvSysUIComponent extends SysUIComponent {
-
-    /**
-     * Builder for a SysUIComponent.
-     */
-    @Subcomponent.Builder
-    interface Builder extends SysUIComponent.Builder {
-        TvSysUIComponent build();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java
deleted file mode 100644
index fabbb2c..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import android.content.Context;
-
-import com.android.systemui.SystemUIInitializer;
-import com.android.systemui.dagger.GlobalRootComponent;
-
-/**
- * TV variant {@link SystemUIInitializer}, that substitutes default {@link GlobalRootComponent} for
- * {@link TvGlobalRootComponent}
- */
-public class TvSystemUIInitializer extends SystemUIInitializer {
-    public TvSystemUIInitializer(Context context) {
-        super(context);
-    }
-
-    @Override
-    protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() {
-        return DaggerTvGlobalRootComponent.builder();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
deleted file mode 100644
index 8cf71a0..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
-import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME;
-
-import android.content.Context;
-import android.hardware.SensorPrivacyManager;
-import android.os.Handler;
-
-import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardViewController;
-import com.android.systemui.dagger.ReferenceSystemUIModule;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dock.DockManager;
-import com.android.systemui.dock.DockManagerImpl;
-import com.android.systemui.doze.DozeHost;
-import com.android.systemui.navigationbar.gestural.GestureModule;
-import com.android.systemui.plugins.qs.QSFactory;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.dagger.PowerModule;
-import com.android.systemui.privacy.MediaProjectionPrivacyItemMonitor;
-import com.android.systemui.privacy.PrivacyItemMonitor;
-import com.android.systemui.qs.dagger.QSModule;
-import com.android.systemui.qs.tileimpl.QSFactoryImpl;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.screenshot.ReferenceScreenshotModule;
-import com.android.systemui.settings.dagger.MultiUserUtilsModule;
-import com.android.systemui.shade.NotificationShadeWindowControllerImpl;
-import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shade.ShadeControllerEmptyImpl;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.KeyboardShortcutsModule;
-import com.android.systemui.statusbar.NotificationListener;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
-import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.events.StatusBarEventsModule;
-import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
-import com.android.systemui.statusbar.phone.DozeServiceHost;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.AospPolicyModule;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.statusbar.policy.HeadsUpManagerLogger;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
-import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler;
-import com.android.systemui.volume.dagger.VolumeModule;
-
-import dagger.Binds;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.IntoSet;
-
-import javax.inject.Named;
-
-/**
- * A TV specific version of {@link ReferenceSystemUIModule}.
- *
- * Code here should be specific to the TV variant of SystemUI and will not be included in other
- * variants of SystemUI.
- */
-@Module(
-        includes = {
-                AospPolicyModule.class,
-                GestureModule.class,
-                MultiUserUtilsModule.class,
-                PowerModule.class,
-                QSModule.class,
-                ReferenceScreenshotModule.class,
-                StatusBarEventsModule.class,
-                VolumeModule.class,
-                KeyboardShortcutsModule.class
-        }
-)
-public abstract class TvSystemUIModule {
-
-    @SysUISingleton
-    @Provides
-    @Named(LEAK_REPORT_EMAIL_NAME)
-    static String provideLeakReportEmail() {
-        return "";
-    }
-
-    @Binds
-    abstract NotificationLockscreenUserManager bindNotificationLockscreenUserManager(
-            NotificationLockscreenUserManagerImpl notificationLockscreenUserManager);
-
-    @Provides
-    @SysUISingleton
-    static SensorPrivacyController provideSensorPrivacyController(
-            SensorPrivacyManager sensorPrivacyManager) {
-        SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager);
-        spC.init();
-        return spC;
-    }
-
-    @Provides
-    @SysUISingleton
-    static IndividualSensorPrivacyController provideIndividualSensorPrivacyController(
-            SensorPrivacyManager sensorPrivacyManager) {
-        IndividualSensorPrivacyController spC = new IndividualSensorPrivacyControllerImpl(
-                sensorPrivacyManager);
-        spC.init();
-        return spC;
-    }
-
-    @Binds
-    @SysUISingleton
-    abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl);
-
-    @Binds
-    abstract DockManager bindDockManager(DockManagerImpl dockManager);
-
-    @Binds
-    abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController);
-
-    @SysUISingleton
-    @Provides
-    @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME)
-    static boolean provideAllowNotificationLongPress() {
-        return true;
-    }
-
-    @SysUISingleton
-    @Provides
-    static HeadsUpManagerPhone provideHeadsUpManagerPhone(
-            Context context,
-            HeadsUpManagerLogger headsUpManagerLogger,
-            StatusBarStateController statusBarStateController,
-            KeyguardBypassController bypassController,
-            GroupMembershipManager groupManager,
-            VisualStabilityProvider visualStabilityProvider,
-            ConfigurationController configurationController,
-            @Main Handler handler,
-            AccessibilityManagerWrapper accessibilityManagerWrapper,
-            UiEventLogger uiEventLogger,
-            ShadeExpansionStateManager shadeExpansionStateManager) {
-        return new HeadsUpManagerPhone(
-                context,
-                headsUpManagerLogger,
-                statusBarStateController,
-                bypassController,
-                groupManager,
-                visualStabilityProvider,
-                configurationController,
-                handler,
-                accessibilityManagerWrapper,
-                uiEventLogger,
-                shadeExpansionStateManager
-        );
-    }
-
-    @Binds
-    abstract HeadsUpManager bindHeadsUpManagerPhone(HeadsUpManagerPhone headsUpManagerPhone);
-
-    @Provides
-    @SysUISingleton
-    static Recents provideRecents(Context context, RecentsImplementation recentsImplementation,
-            CommandQueue commandQueue) {
-        return new Recents(context, recentsImplementation, commandQueue);
-    }
-
-    @SysUISingleton
-    @Provides
-    static DeviceProvisionedController providesDeviceProvisionedController(
-            DeviceProvisionedControllerImpl deviceProvisionedController) {
-        deviceProvisionedController.init();
-        return deviceProvisionedController;
-    }
-
-    @Binds
-    abstract KeyguardViewController bindKeyguardViewController(
-            StatusBarKeyguardViewManager statusBarKeyguardViewManager);
-
-    @Binds
-    abstract NotificationShadeWindowController bindNotificationShadeController(
-            NotificationShadeWindowControllerImpl notificationShadeWindowController);
-
-    @Binds
-    abstract DozeHost provideDozeHost(DozeServiceHost dozeServiceHost);
-
-    @Provides
-    @SysUISingleton
-    static TvNotificationHandler provideTvNotificationHandler(
-            NotificationListener notificationListener) {
-        return new TvNotificationHandler(notificationListener);
-    }
-
-    /**
-     * Binds {@link MediaProjectionPrivacyItemMonitor} into the set of {@link PrivacyItemMonitor}.
-     */
-    @Binds
-    @IntoSet
-    abstract PrivacyItemMonitor bindMediaProjectionPrivacyItemMonitor(
-            MediaProjectionPrivacyItemMonitor mediaProjectionPrivacyItemMonitor);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
deleted file mode 100644
index 8370615..0000000
--- a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.tv;
-
-import com.android.systemui.dagger.WMComponent;
-import com.android.wm.shell.dagger.WMSingleton;
-import com.android.wm.shell.dagger.TvWMShellModule;
-
-import dagger.Subcomponent;
-
-
-/**
- * Dagger Subcomponent for WindowManager.
- */
-@WMSingleton
-@Subcomponent(modules = {TvWMShellModule.class})
-public interface TvWMComponent extends WMComponent {
-
-    /**
-     * Builder for a SysUIComponent.
-     */
-    @Subcomponent.Builder
-    interface Builder extends WMComponent.Builder {
-        TvWMComponent build();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java
deleted file mode 100644
index b03bedc..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbConfirmActivity.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2021 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.usb.tv;
-
-import com.android.systemui.R;
-
-/**
- * Dialog shown to confirm the package to start when a USB device or accessory is attached and there
- * is only one package that claims to handle this USB device or accessory.
- */
-public class TvUsbConfirmActivity extends TvUsbDialogActivity {
-    private static final String TAG = TvUsbConfirmActivity.class.getSimpleName();
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        final int strId;
-        if (mDialogHelper.isUsbDevice()) {
-            boolean useRecordWarning = mDialogHelper.deviceHasAudioCapture()
-                    && !mDialogHelper.packageHasAudioRecordingPermission();
-            strId = useRecordWarning
-                    ? R.string.usb_device_confirm_prompt_warn
-                    : R.string.usb_device_confirm_prompt;
-        } else {
-            // UsbAccessory case
-            strId = R.string.usb_accessory_confirm_prompt;
-        }
-        CharSequence text = getString(strId, mDialogHelper.getAppName(),
-                mDialogHelper.getDeviceDescription());
-        initUI(mDialogHelper.getAppName(), text);
-    }
-
-    @Override
-    void onConfirm() {
-        mDialogHelper.grantUidAccessPermission();
-        mDialogHelper.confirmDialogStartActivity();
-        finish();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java
deleted file mode 100644
index 1c003ea..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbDialogActivity.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2021 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.usb.tv;
-
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-import com.android.systemui.tv.TvBottomSheetActivity;
-import com.android.systemui.usb.UsbDialogHelper;
-
-abstract class TvUsbDialogActivity extends TvBottomSheetActivity implements View.OnClickListener {
-    private static final String TAG = TvUsbDialogActivity.class.getSimpleName();
-    UsbDialogHelper mDialogHelper;
-
-    @Override
-    public final void onCreate(Bundle b) {
-        super.onCreate(b);
-        getWindow().addPrivateFlags(
-                WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
-        try {
-            mDialogHelper = new UsbDialogHelper(getApplicationContext(), getIntent());
-        } catch (IllegalStateException e) {
-            Log.e(TAG, "unable to initialize", e);
-            finish();
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mDialogHelper.registerUsbDisconnectedReceiver(this);
-    }
-
-    @Override
-    protected void onPause() {
-        if (mDialogHelper != null) {
-            mDialogHelper.unregisterUsbDisconnectedReceiver(this);
-        }
-        super.onPause();
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v.getId() == R.id.bottom_sheet_positive_button) {
-            onConfirm();
-        } else {
-            finish();
-        }
-    }
-
-    /**
-     * Called when the ok button is clicked.
-     */
-    abstract void onConfirm();
-
-    void initUI(CharSequence title, CharSequence text) {
-        TextView titleTextView = findViewById(R.id.bottom_sheet_title);
-        TextView contentTextView = findViewById(R.id.bottom_sheet_body);
-        ImageView icon = findViewById(R.id.bottom_sheet_icon);
-        ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
-        Button okButton = findViewById(R.id.bottom_sheet_positive_button);
-        Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
-
-        titleTextView.setText(title);
-        contentTextView.setText(text);
-        icon.setImageResource(com.android.internal.R.drawable.ic_usb_48dp);
-        secondIcon.setVisibility(View.GONE);
-        okButton.setText(android.R.string.ok);
-        okButton.setOnClickListener(this);
-
-        cancelButton.setText(android.R.string.cancel);
-        cancelButton.setOnClickListener(this);
-        cancelButton.requestFocus();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java
deleted file mode 100644
index 7b415b7..0000000
--- a/packages/SystemUI/src/com/android/systemui/usb/tv/TvUsbPermissionActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 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.usb.tv;
-
-import com.android.systemui.R;
-
-/**
- * Dialog shown when a package requests access to a USB device or accessory on TVs.
- */
-public class TvUsbPermissionActivity extends TvUsbDialogActivity {
-    private static final String TAG = TvUsbPermissionActivity.class.getSimpleName();
-
-    private boolean mPermissionGranted = false;
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        final int strId;
-        if (mDialogHelper.isUsbDevice()) {
-            boolean useRecordWarning = mDialogHelper.deviceHasAudioCapture()
-                    && !mDialogHelper.packageHasAudioRecordingPermission();
-            strId = useRecordWarning
-                    ? R.string.usb_device_permission_prompt_warn
-                    : R.string.usb_device_permission_prompt;
-        } else {
-            // UsbAccessory case
-            strId = R.string.usb_accessory_permission_prompt;
-        }
-        CharSequence text = getString(strId, mDialogHelper.getAppName(),
-                mDialogHelper.getDeviceDescription());
-        initUI(mDialogHelper.getAppName(), text);
-    }
-
-    @Override
-    protected void onPause() {
-        if (isFinishing()) {
-            mDialogHelper.sendPermissionDialogResponse(mPermissionGranted);
-        }
-        super.onPause();
-    }
-
-    @Override
-    void onConfirm() {
-        mDialogHelper.grantUidAccessPermission();
-        mPermissionGranted = true;
-        finish();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index 693c270..5582ced 100644
--- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
@@ -23,7 +23,6 @@
  * changes.
  */
 interface UserAwareController {
-    @JvmDefault
     fun changeUser(newUser: UserHandle) {}
 
     val currentUserId: Int
diff --git a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
index db2aca8..65a0218 100644
--- a/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
@@ -16,32 +16,34 @@
 
 package com.android.systemui.util
 
-import android.app.WallpaperInfo
 import android.app.WallpaperManager
 import android.util.Log
 import android.view.View
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.wallpapers.data.repository.WallpaperRepository
 import javax.inject.Inject
 import kotlin.math.max
 
 private const val TAG = "WallpaperController"
 
+/**
+ * Controller for wallpaper-related logic.
+ *
+ * Note: New logic should be added to [WallpaperRepository], not this class.
+ */
 @SysUISingleton
-class WallpaperController @Inject constructor(private val wallpaperManager: WallpaperManager) {
+class WallpaperController @Inject constructor(
+    private val wallpaperManager: WallpaperManager,
+    private val wallpaperRepository: WallpaperRepository,
+) {
 
     var rootView: View? = null
 
     private var notificationShadeZoomOut: Float = 0f
     private var unfoldTransitionZoomOut: Float = 0f
 
-    private var wallpaperInfo: WallpaperInfo? = null
-
-    fun onWallpaperInfoUpdated(wallpaperInfo: WallpaperInfo?) {
-        this.wallpaperInfo = wallpaperInfo
-    }
-
     private val shouldUseDefaultUnfoldTransition: Boolean
-        get() = wallpaperInfo?.shouldUseDefaultUnfoldTransition()
+        get() = wallpaperRepository.wallpaperInfo.value?.shouldUseDefaultUnfoldTransition()
             ?: true
 
     fun setNotificationShadeZoom(zoomOut: Float) {
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
index a640589..b45b8cd 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt
@@ -16,9 +16,11 @@
 
 package com.android.systemui.wallpapers.data.repository
 
+import android.app.WallpaperInfo
 import com.android.systemui.dagger.SysUISingleton
 import javax.inject.Inject
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
 /**
@@ -29,5 +31,6 @@
  */
 @SysUISingleton
 class NoopWallpaperRepository @Inject constructor() : WallpaperRepository {
+    override val wallpaperInfo: StateFlow<WallpaperInfo?> = MutableStateFlow(null).asStateFlow()
     override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
index 48895ff..b8f9583 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.wallpapers.data.repository
 
+import android.app.WallpaperInfo
 import android.app.WallpaperManager
 import android.content.Context
 import android.content.Intent
@@ -36,11 +37,15 @@
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 
 /** A repository storing information about the current wallpaper. */
 interface WallpaperRepository {
+    /** Emits the current user's current wallpaper. */
+    val wallpaperInfo: StateFlow<WallpaperInfo?>
+
     /** Emits true if the current user's current wallpaper supports ambient mode. */
     val wallpaperSupportsAmbientMode: StateFlow<Boolean>
 }
@@ -78,28 +83,35 @@
             // Only update the wallpaper status once the user selection has finished.
             .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE }
 
-    override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+    override val wallpaperInfo: StateFlow<WallpaperInfo?> =
         if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) {
-            MutableStateFlow(false).asStateFlow()
+            MutableStateFlow(null).asStateFlow()
         } else {
             combine(wallpaperChanged, selectedUser) { _, selectedUser ->
-                    doesWallpaperSupportAmbientMode(selectedUser)
+                    getWallpaper(selectedUser)
                 }
                 .stateIn(
                     scope,
                     // Always be listening for wallpaper changes.
                     SharingStarted.Eagerly,
-                    initialValue =
-                        doesWallpaperSupportAmbientMode(userRepository.selectedUser.value),
+                    initialValue = getWallpaper(userRepository.selectedUser.value),
                 )
         }
 
-    private fun doesWallpaperSupportAmbientMode(selectedUser: SelectedUserModel): Boolean {
-        return wallpaperManager
-            .getWallpaperInfoForUser(
-                selectedUser.userInfo.id,
+    override val wallpaperSupportsAmbientMode: StateFlow<Boolean> =
+        wallpaperInfo
+            .map {
+                // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
+                it?.supportsAmbientMode() == true
+            }
+            .stateIn(
+                scope,
+                // Always be listening for wallpaper changes.
+                SharingStarted.Eagerly,
+                initialValue = wallpaperInfo.value?.supportsAmbientMode() == true,
             )
-            // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode.
-            ?.supportsAmbientMode() == true
+
+    private fun getWallpaper(selectedUser: SelectedUserModel): WallpaperInfo? {
+        return wallpaperManager.getWallpaperInfoForUser(selectedUser.userInfo.id)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index a5365fb..2acd4b9 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -621,13 +621,9 @@
     }
 
     private boolean isDismissableFromBubbles(NotificationEntry e) {
-        if (mNotifPipelineFlags.allowDismissOngoing()) {
-            // Bubbles are only accessible from the unlocked state,
-            // so we can calculate this from the Notification flags only.
-            return e.isDismissableForState(/*isLocked=*/ false);
-        } else {
-            return e.legacyIsDismissableRecursive();
-        }
+        // Bubbles are only accessible from the unlocked state,
+        // so we can calculate this from the Notification flags only.
+        return e.isDismissableForState(/*isLocked=*/ false);
     }
 
     private boolean shouldBubbleUp(NotificationEntry e) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 5d75428..cb18229 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -76,7 +76,7 @@
   private lateinit var mKeyguardMessageAreaController:
       KeyguardMessageAreaController<BouncerKeyguardMessageArea>
 
-    @Mock private lateinit var mPostureController: DevicePostureController
+  @Mock private lateinit var mPostureController: DevicePostureController
 
   private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
   private lateinit var fakeFeatureFlags: FakeFeatureFlags
@@ -119,7 +119,7 @@
 
         mKeyguardPatternViewController.onViewAttached()
 
-        assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+        assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
     }
 
     @Test
@@ -131,15 +131,20 @@
         mKeyguardPatternViewController.onViewAttached()
 
         // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
-        assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+        assertThat(getPatternTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
 
         // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
         verify(mPostureController).addCallback(postureCallbackCaptor.capture())
         val postureCallback: DevicePostureController.Callback = postureCallbackCaptor.value
         postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
 
-        // Verify view is now in posture state DEVICE_POSTURE_OPENED
-        assertThat(getPatternTopGuideline()).isNotEqualTo(getExpectedTopGuideline())
+        // Simulate posture change to same state with callback
+        assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+
+        postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+        // Verify view is still in posture state DEVICE_POSTURE_OPENED
+        assertThat(getPatternTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
     }
 
     private fun getPatternTopGuideline(): Float {
@@ -150,7 +155,7 @@
         return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
     }
 
-    private fun getExpectedTopGuideline(): Float {
+    private fun getHalfOpenedBouncerHeightRatio(): Float {
         return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d256ee1..4dc7652 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -19,6 +19,8 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.filters.SmallTest
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
@@ -32,6 +34,8 @@
 import com.android.systemui.statusbar.policy.DevicePostureController
 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -51,7 +55,10 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class KeyguardPinViewControllerTest : SysuiTestCase() {
-    @Mock private lateinit var keyguardPinView: KeyguardPINView
+
+    private lateinit var objectKeyguardPINView: KeyguardPINView
+
+    @Mock private lateinit var mockKeyguardPinView: KeyguardPINView
 
     @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
 
@@ -83,64 +90,73 @@
     @Mock lateinit var deleteButton: NumPadButton
     @Mock lateinit var enterButton: View
 
-    private lateinit var pinViewController: KeyguardPinViewController
-
     @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
-        Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+        Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
             .thenReturn(keyguardMessageArea)
         Mockito.`when`(
                 keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
             )
             .thenReturn(keyguardMessageAreaController)
-        `when`(keyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
-        `when`(keyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
+        `when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
+        `when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
             .thenReturn(passwordTextView)
-        `when`(keyguardPinView.resources).thenReturn(context.resources)
-        `when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
+        `when`(mockKeyguardPinView.resources).thenReturn(context.resources)
+        `when`(mockKeyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
             .thenReturn(deleteButton)
-        `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
+        `when`(mockKeyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
         // For posture tests:
-        `when`(keyguardPinView.buttons).thenReturn(arrayOf())
+        `when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
         `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
 
-        pinViewController =
-            KeyguardPinViewController(
-                keyguardPinView,
-                keyguardUpdateMonitor,
-                securityMode,
-                lockPatternUtils,
-                mKeyguardSecurityCallback,
-                keyguardMessageAreaControllerFactory,
-                mLatencyTracker,
-                liftToActivateListener,
-                mEmergencyButtonController,
-                falsingCollector,
-                postureController,
-                featureFlags
-            )
+        objectKeyguardPINView =
+            View.inflate(mContext, R.layout.keyguard_pin_view, null)
+                .findViewById(R.id.keyguard_pin_view) as KeyguardPINView
+    }
+
+    private fun constructPinViewController(
+        mKeyguardPinView: KeyguardPINView
+    ): KeyguardPinViewController {
+        return KeyguardPinViewController(
+            mKeyguardPinView,
+            keyguardUpdateMonitor,
+            securityMode,
+            lockPatternUtils,
+            mKeyguardSecurityCallback,
+            keyguardMessageAreaControllerFactory,
+            mLatencyTracker,
+            liftToActivateListener,
+            mEmergencyButtonController,
+            falsingCollector,
+            postureController,
+            featureFlags
+        )
     }
 
     @Test
-    fun onViewAttached_deviceHalfFolded_propagatedToPinView() {
-        `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+    fun onViewAttached_deviceHalfFolded_propagatedToPatternView() {
+        val pinViewController = constructPinViewController(objectKeyguardPINView)
+        overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
+        whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
 
         pinViewController.onViewAttached()
 
-        verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
+        assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
     }
 
     @Test
-    fun onDevicePostureChanged_deviceHalfFolded_propagatedToPinView() {
-        `when`(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+    fun onDevicePostureChanged_deviceOpened_propagatedToPatternView() {
+        val pinViewController = constructPinViewController(objectKeyguardPINView)
+        overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
+
+        whenever(postureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+        pinViewController.onViewAttached()
 
         // Verify view begins in posture state DEVICE_POSTURE_HALF_OPENED
-        pinViewController.onViewAttached()
-
-        verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_HALF_OPENED)
+        assertThat(getPinTopGuideline()).isEqualTo(getHalfOpenedBouncerHeightRatio())
 
         // Simulate posture change to state DEVICE_POSTURE_OPENED with callback
         verify(postureController).addCallback(postureCallbackCaptor.capture())
@@ -148,31 +164,57 @@
         postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
 
         // Verify view is now in posture state DEVICE_POSTURE_OPENED
-        verify(keyguardPinView).onDevicePostureChanged(DEVICE_POSTURE_OPENED)
+        assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+
+        // Simulate posture change to same state with callback
+        postureCallback.onPostureChanged(DEVICE_POSTURE_OPENED)
+
+        // Verify view is still in posture state DEVICE_POSTURE_OPENED
+        assertThat(getPinTopGuideline()).isNotEqualTo(getHalfOpenedBouncerHeightRatio())
+    }
+
+    private fun getPinTopGuideline(): Float {
+        val cs = ConstraintSet()
+        val container = objectKeyguardPINView.findViewById(R.id.pin_container) as ConstraintLayout
+        cs.clone(container)
+        return cs.getConstraint(R.id.pin_pad_top_guideline).layout.guidePercent
+    }
+
+    private fun getHalfOpenedBouncerHeightRatio(): Float {
+        return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
     }
 
     @Test
     fun startAppearAnimation() {
+        val pinViewController = constructPinViewController(mockKeyguardPinView)
+
         pinViewController.startAppearAnimation()
+
         verify(keyguardMessageAreaController)
             .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false)
     }
 
     @Test
     fun startAppearAnimation_withExistingMessage() {
+        val pinViewController = constructPinViewController(mockKeyguardPinView)
         Mockito.`when`(keyguardMessageAreaController.message).thenReturn("Unlock to continue.")
+
         pinViewController.startAppearAnimation()
+
         verify(keyguardMessageAreaController, Mockito.never()).setMessage(anyString(), anyBoolean())
     }
 
     @Test
     fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
+        val pinViewController = constructPinViewController(mockKeyguardPinView)
         `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+        `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
         `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
         `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
         `when`(passwordTextView.text).thenReturn("")
 
         pinViewController.startAppearAnimation()
+
         verify(deleteButton).visibility = View.INVISIBLE
         verify(enterButton).visibility = View.INVISIBLE
         verify(passwordTextView).setUsePinShapes(true)
@@ -181,12 +223,15 @@
 
     @Test
     fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
+        val pinViewController = constructPinViewController(mockKeyguardPinView)
         `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+        `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
         `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
         `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
         `when`(passwordTextView.text).thenReturn("")
 
         pinViewController.startAppearAnimation()
+
         verify(deleteButton).visibility = View.VISIBLE
         verify(enterButton).visibility = View.VISIBLE
         verify(passwordTextView).setUsePinShapes(true)
@@ -195,7 +240,10 @@
 
     @Test
     fun handleLockout_readsNumberOfErrorAttempts() {
+        val pinViewController = constructPinViewController(mockKeyguardPinView)
+
         pinViewController.handleAttemptLockout(0)
+
         verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 512e5dc..7114c22 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -27,6 +27,7 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.keyguard.logging.KeyguardLogger;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.ClockConfig;
 import com.android.systemui.plugins.ClockController;
@@ -62,6 +63,8 @@
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private InteractionJankMonitor mInteractionJankMonitor;
 
+    @Mock private DumpManager mDumpManager;
+
     @Captor
     private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
 
@@ -82,7 +85,8 @@
                 mScreenOffAnimationController,
                 mKeyguardLogger,
                 mFeatureFlags,
-                mInteractionJankMonitor) {
+                mInteractionJankMonitor,
+                mDumpManager) {
                     @Override
                     void setProperty(
                             AnimatableProperty property,
@@ -170,4 +174,12 @@
         verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(false);
         verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true);
     }
+
+    @Test
+    public void correctlyDump() {
+        mController.onInit();
+        verify(mDumpManager).registerDumpable(mController);
+        mController.onDestroy();
+        verify(mDumpManager, times(1)).unregisterDumpable(KeyguardStatusViewController.TAG);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
index 6decb88..5867a40c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ExpandHelperTest.java
@@ -30,6 +30,8 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -43,12 +45,14 @@
 @RunWithLooper
 public class ExpandHelperTest extends SysuiTestCase {
 
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     private ExpandableNotificationRow mRow;
     private ExpandHelper mExpandHelper;
     private ExpandHelper.Callback mCallback;
 
     @Before
     public void setUp() throws Exception {
+        mFeatureFlags.setDefault(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION);
         mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
         mDependency.injectMockDependency(NotificationMediaManager.class);
         allowTestableLooperAsMainThread();
@@ -56,7 +60,8 @@
         NotificationTestHelper helper = new NotificationTestHelper(
                 mContext,
                 mDependency,
-                TestableLooper.get(this));
+                TestableLooper.get(this),
+                mFeatureFlags);
         mRow = helper.createRow();
         mCallback = mock(ExpandHelper.Callback.class);
         mExpandHelper = new ExpandHelper(context, mCallback, 10, 100);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index eaa31ac..ecc776b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -48,6 +48,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.airbnb.lottie.LottieAnimationView
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
@@ -64,7 +65,6 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import com.android.systemui.util.concurrency.FakeExecutor
@@ -153,8 +153,8 @@
                 mock(KeyguardStateController::class.java),
                 keyguardBouncerRepository,
                 FakeBiometricSettingsRepository(),
-                FakeDeviceEntryFingerprintAuthRepository(),
                 FakeSystemClock(),
+                mock(KeyguardUpdateMonitor::class.java),
             )
         displayStateInteractor =
             DisplayStateInteractorImpl(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 9df06dc..8dfeb3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
-import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -106,8 +105,8 @@
                 mock(KeyguardStateController::class.java),
                 keyguardBouncerRepository,
                 mock(BiometricSettingsRepository::class.java),
-                mock(DeviceEntryFingerprintAuthRepository::class.java),
                 mock(SystemClock::class.java),
+                mKeyguardUpdateMonitor,
             )
         return createUdfpsKeyguardViewController(
             /* useModernBouncer */ true, /* useExpandedOverlay */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
new file mode 100644
index 0000000..f9b590f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptHistoryImplTest.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.biometrics.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.shared.model.BiometricModality
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class PromptHistoryImplTest : SysuiTestCase() {
+
+    private lateinit var history: PromptHistoryImpl
+
+    @Before
+    fun setup() {
+        history = PromptHistoryImpl()
+    }
+
+    @Test
+    fun empty() {
+        assertThat(history.faceFailed).isFalse()
+        assertThat(history.fingerprintFailed).isFalse()
+    }
+
+    @Test
+    fun faceFailed() =
+        repeat(2) {
+            history.failure(BiometricModality.None)
+            history.failure(BiometricModality.Face)
+
+            assertThat(history.faceFailed).isTrue()
+            assertThat(history.fingerprintFailed).isFalse()
+        }
+
+    @Test
+    fun fingerprintFailed() =
+        repeat(2) {
+            history.failure(BiometricModality.None)
+            history.failure(BiometricModality.Fingerprint)
+
+            assertThat(history.faceFailed).isFalse()
+            assertThat(history.fingerprintFailed).isTrue()
+        }
+
+    @Test
+    fun coexFailed() =
+        repeat(2) {
+            history.failure(BiometricModality.Face)
+            history.failure(BiometricModality.Fingerprint)
+
+            assertThat(history.faceFailed).isTrue()
+            assertThat(history.fingerprintFailed).isTrue()
+
+            history.failure(BiometricModality.None)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 91140a9..40b1f20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -335,7 +335,7 @@
                     error,
                     messageAfterError = "or me",
                     authenticateAfterError = false,
-                    suppressIf = { _ -> true },
+                    suppressIf = { _, _ -> true },
                 )
             }
         }
@@ -364,7 +364,7 @@
                     error,
                     messageAfterError = "$error $afterSuffix",
                     authenticateAfterError = false,
-                    suppressIf = { currentMessage -> suppress && currentMessage.isError },
+                    suppressIf = { currentMessage, _ -> suppress && currentMessage.isError },
                 )
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index 37b9ca4..186df02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
@@ -18,6 +18,7 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
@@ -54,6 +55,7 @@
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var systemClock: SystemClock
     @Mock private lateinit var bouncerLogger: TableLogBuffer
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
     @Before
     fun setup() {
@@ -72,8 +74,8 @@
                 keyguardStateController,
                 bouncerRepository,
                 biometricSettingsRepository,
-                deviceEntryFingerprintAuthRepository,
                 systemClock,
+                keyguardUpdateMonitor,
             )
     }
 
@@ -118,7 +120,7 @@
     @Test
     fun canShowAlternateBouncerForFingerprint_fingerprintLockedOut() {
         givenCanShowAlternateBouncer()
-        deviceEntryFingerprintAuthRepository.setLockedOut(true)
+        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
 
         assertFalse(underTest.canShowAlternateBouncerForFingerprint())
     }
@@ -168,7 +170,7 @@
         biometricSettingsRepository.setFingerprintEnrolled(true)
         biometricSettingsRepository.setStrongBiometricAllowed(true)
         biometricSettingsRepository.setFingerprintEnabledByDevicePolicy(true)
-        deviceEntryFingerprintAuthRepository.setLockedOut(false)
+        whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(false)
         whenever(keyguardStateController.isUnlocked).thenReturn(false)
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
index 461ec65..40f0ed3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java
@@ -28,10 +28,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.dreams.DreamLogger;
 import com.android.systemui.dreams.DreamOverlayStateController;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.log.core.FakeLogBuffer;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -57,8 +57,6 @@
     private FakeFeatureFlags mFeatureFlags;
     @Mock
     private Observer mObserver;
-    @Mock
-    private DreamLogger mLogger;
 
     @Before
     public void setUp() {
@@ -70,7 +68,7 @@
                 mExecutor,
                 /* overlayEnabled= */ true,
                 mFeatureFlags,
-                mLogger);
+                FakeLogBuffer.Factory.Companion.create());
         mLiveData = new ComplicationCollectionLiveData(mStateController);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
index a00e545..57307fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayAnimationsControllerTest.kt
@@ -9,6 +9,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.complication.ComplicationHostViewController
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
+import com.android.systemui.log.core.FakeLogBuffer
 import com.android.systemui.statusbar.BlurUtils
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.mockito.argumentCaptor
@@ -47,7 +48,7 @@
     @Mock private lateinit var stateController: DreamOverlayStateController
     @Mock private lateinit var configController: ConfigurationController
     @Mock private lateinit var transitionViewModel: DreamingToLockscreenTransitionViewModel
-    @Mock private lateinit var logger: DreamLogger
+    private val logBuffer = FakeLogBuffer.Factory.create()
     private lateinit var controller: DreamOverlayAnimationsController
 
     @Before
@@ -66,7 +67,7 @@
                 DREAM_IN_COMPLICATIONS_ANIMATION_DURATION,
                 DREAM_IN_TRANSLATION_Y_DISTANCE,
                 DREAM_IN_TRANSLATION_Y_DURATION,
-                logger
+                logBuffer
             )
 
         val mockView: View = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 2c1ebe4..44a78ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -34,6 +34,8 @@
 import com.android.systemui.complication.Complication;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -58,8 +60,7 @@
     @Mock
     private FeatureFlags mFeatureFlags;
 
-    @Mock
-    private DreamLogger mLogger;
+    private final LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create();
 
     final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -408,6 +409,11 @@
     }
 
     private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) {
-        return new DreamOverlayStateController(mExecutor, overlayEnabled, mFeatureFlags, mLogger);
+        return new DreamOverlayStateController(
+                mExecutor,
+                overlayEnabled,
+                mFeatureFlags,
+                mLogBuffer
+        );
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 5dc0e55..4e74f451 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -48,6 +48,8 @@
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.log.LogBuffer;
+import com.android.systemui.log.core.FakeLogBuffer;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
@@ -113,8 +115,8 @@
     DreamOverlayStateController mDreamOverlayStateController;
     @Mock
     UserTracker mUserTracker;
-    @Mock
-    DreamLogger mLogger;
+
+    LogBuffer mLogBuffer = FakeLogBuffer.Factory.Companion.create();
 
     @Captor
     private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
@@ -149,7 +151,7 @@
                 mDreamOverlayStatusBarItemsProvider,
                 mDreamOverlayStateController,
                 mUserTracker,
-                mLogger);
+                mLogBuffer);
     }
 
     @Test
@@ -293,7 +295,7 @@
                 mDreamOverlayStatusBarItemsProvider,
                 mDreamOverlayStateController,
                 mUserTracker,
-                mLogger);
+                mLogBuffer);
         controller.onViewAttached();
         verify(mView, never()).showIcon(
                 eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
deleted file mode 100644
index eb998cc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.hdmi;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.SecureSettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Locale;
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-public class HdmiCecSetMenuLanguageHelperTest extends SysuiTestCase {
-
-    private HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
-
-    @Mock
-    private Executor mExecutor;
-
-    @Mock
-    private SecureSettings mSecureSettings;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mSecureSettings.getStringForUser(
-                Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
-                UserHandle.USER_CURRENT)).thenReturn(null);
-        mHdmiCecSetMenuLanguageHelper =
-                new HdmiCecSetMenuLanguageHelper(mExecutor, mSecureSettings);
-    }
-
-    @Test
-    public void testSetGetLocale() {
-        mHdmiCecSetMenuLanguageHelper.setLocale("en");
-        assertThat(mHdmiCecSetMenuLanguageHelper.getLocale()).isEqualTo(Locale.ENGLISH);
-    }
-
-    @Test
-    public void testIsLocaleDenylisted_EmptyByDefault() {
-        mHdmiCecSetMenuLanguageHelper.setLocale("en");
-        assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
-    }
-
-    @Test
-    public void testIsLocaleDenylisted_AcceptLanguage() {
-        mHdmiCecSetMenuLanguageHelper.setLocale("de");
-        mHdmiCecSetMenuLanguageHelper.acceptLocale();
-        assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
-        verify(mExecutor).execute(any());
-    }
-
-    @Test
-    public void testIsLocaleDenylisted_DeclineLanguage() {
-        mHdmiCecSetMenuLanguageHelper.setLocale("de");
-        mHdmiCecSetMenuLanguageHelper.declineLocale();
-        assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
-        verify(mSecureSettings).putStringForUser(
-                Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de",
-                UserHandle.USER_CURRENT);
-    }
-
-    @Test
-    public void testIsLocaleDenylisted_DeclineTwoLanguages() {
-        mHdmiCecSetMenuLanguageHelper.setLocale("de");
-        mHdmiCecSetMenuLanguageHelper.declineLocale();
-        assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
-        verify(mSecureSettings).putStringForUser(
-                Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de",
-                UserHandle.USER_CURRENT);
-        mHdmiCecSetMenuLanguageHelper.setLocale("pl");
-        mHdmiCecSetMenuLanguageHelper.declineLocale();
-        assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
-        verify(mSecureSettings).putStringForUser(
-                Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de,pl",
-                UserHandle.USER_CURRENT);
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index ddcbe79..00f67a3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -78,6 +78,7 @@
 import com.android.keyguard.KeyguardSecurityView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TestScopeProvider;
 import com.android.keyguard.mediator.ScreenOnCoordinator;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.SysuiTestCase;
@@ -115,9 +116,11 @@
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.DeviceConfigProxyFake;
 import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.settings.SystemSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository;
 import com.android.wm.shell.keyguard.KeyguardTransitions;
 
 import org.junit.After;
@@ -132,6 +135,7 @@
 
 import kotlinx.coroutines.CoroutineDispatcher;
 import kotlinx.coroutines.flow.Flow;
+import kotlinx.coroutines.test.TestScope;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -139,6 +143,9 @@
 public class KeyguardViewMediatorTest extends SysuiTestCase {
     private KeyguardViewMediator mViewMediator;
 
+    private final TestScope mTestScope = TestScopeProvider.getTestScope();
+    private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
+
     private @Mock UserTracker mUserTracker;
     private @Mock DevicePolicyManager mDevicePolicyManager;
     private @Mock LockPatternUtils mLockPatternUtils;
@@ -191,6 +198,7 @@
     private @Mock SecureSettings mSecureSettings;
     private @Mock AlarmManager mAlarmManager;
     private FakeSystemClock mSystemClock;
+    private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
 
     /** Most recent value passed to {@link KeyguardStateController#notifyKeyguardGoingAway}. */
     private boolean mKeyguardGoingAway = false;
@@ -928,6 +936,8 @@
                 mKeyguardTransitions,
                 mInteractionJankMonitor,
                 mDreamOverlayStateController,
+                mJavaAdapter,
+                mWallpaperRepository,
                 () -> mShadeController,
                 () -> mNotificationShadeWindowController,
                 () -> mActivityLaunchAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index f9070b3..c6a2fa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -162,11 +162,11 @@
     @Test
     fun convenienceBiometricAllowedChange() =
         testScope.runTest {
+            overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
             createBiometricSettingsRepository()
             val convenienceBiometricAllowed =
                 collectLastValue(underTest.isNonStrongBiometricAllowed)
             runCurrent()
-
             onNonStrongAuthChanged(true, PRIMARY_USER_ID)
             assertThat(convenienceBiometricAllowed()).isTrue()
 
@@ -175,6 +175,45 @@
 
             onNonStrongAuthChanged(false, PRIMARY_USER_ID)
             assertThat(convenienceBiometricAllowed()).isFalse()
+            mContext.orCreateTestableResources.removeOverride(
+                com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+            )
+        }
+
+    @Test
+    fun whenStrongAuthRequiredAfterBoot_nonStrongBiometricNotAllowed() =
+        testScope.runTest {
+            overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, true)
+            createBiometricSettingsRepository()
+
+            val convenienceBiometricAllowed =
+                collectLastValue(underTest.isNonStrongBiometricAllowed)
+            runCurrent()
+            onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+
+            assertThat(convenienceBiometricAllowed()).isFalse()
+            mContext.orCreateTestableResources.removeOverride(
+                com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+            )
+        }
+
+    @Test
+    fun whenStrongBiometricAuthIsNotAllowed_nonStrongBiometrics_alsoNotAllowed() =
+        testScope.runTest {
+            overrideResource(com.android.internal.R.bool.config_strongAuthRequiredOnBoot, false)
+            createBiometricSettingsRepository()
+
+            val convenienceBiometricAllowed =
+                collectLastValue(underTest.isNonStrongBiometricAllowed)
+            runCurrent()
+            onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isTrue()
+
+            onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, PRIMARY_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isFalse()
+            mContext.orCreateTestableResources.removeOverride(
+                com.android.internal.R.bool.config_strongAuthRequiredOnBoot
+            )
         }
 
     private fun onStrongAuthChanged(flags: Int, userId: Int) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index 020c0b2..8127ac6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -36,6 +36,7 @@
 import com.android.keyguard.FaceAuthUiEvent
 import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
 import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.R
 import com.android.systemui.RoboPilotTest
 import com.android.systemui.SysuiTestCase
@@ -43,6 +44,7 @@
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.dump.logcatLogBuffer
 import com.android.systemui.flags.FakeFeatureFlags
@@ -50,12 +52,12 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
+import com.android.systemui.keyguard.shared.model.HelpFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -113,6 +115,7 @@
     @Mock private lateinit var sessionTracker: SessionTracker
     @Mock private lateinit var uiEventLogger: UiEventLogger
     @Mock private lateinit var dumpManager: DumpManager
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
 
     @Captor
     private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
@@ -131,8 +134,8 @@
     private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
     private lateinit var testScope: TestScope
     private lateinit var fakeUserRepository: FakeUserRepository
-    private lateinit var authStatus: FlowValue<AuthenticationStatus?>
-    private lateinit var detectStatus: FlowValue<DetectionStatus?>
+    private lateinit var authStatus: FlowValue<FaceAuthenticationStatus?>
+    private lateinit var detectStatus: FlowValue<FaceDetectionStatus?>
     private lateinit var authRunning: FlowValue<Boolean?>
     private lateinit var bypassEnabled: FlowValue<Boolean?>
     private lateinit var lockedOut: FlowValue<Boolean?>
@@ -175,10 +178,10 @@
             AlternateBouncerInteractor(
                 bouncerRepository = bouncerRepository,
                 biometricSettingsRepository = biometricSettingsRepository,
-                deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
                 systemClock = mock(SystemClock::class.java),
                 keyguardStateController = FakeKeyguardStateController(),
                 statusBarStateController = mock(StatusBarStateController::class.java),
+                keyguardUpdateMonitor = keyguardUpdateMonitor,
             )
 
         bypassStateChangedListener =
@@ -262,7 +265,7 @@
             val successResult = successResult()
             authenticationCallback.value.onAuthenticationSucceeded(successResult)
 
-            assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+            assertThat(authStatus()).isEqualTo(SuccessFaceAuthenticationStatus(successResult))
             assertThat(authenticated()).isTrue()
             assertThat(authRunning()).isFalse()
             assertThat(canFaceAuthRun()).isFalse()
@@ -383,7 +386,10 @@
 
             detectionCallback.value.onFaceDetected(1, 1, true)
 
-            assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+            val status = detectStatus()!!
+            assertThat(status.sensorId).isEqualTo(1)
+            assertThat(status.userId).isEqualTo(1)
+            assertThat(status.isStrongBiometric).isEqualTo(true)
         }
 
     @Test
@@ -423,7 +429,7 @@
                 FACE_ERROR_CANCELED,
                 "First auth attempt cancellation completed"
             )
-            val value = authStatus() as ErrorAuthenticationStatus
+            val value = authStatus() as ErrorFaceAuthenticationStatus
             assertThat(value.msgId).isEqualTo(FACE_ERROR_CANCELED)
             assertThat(value.msg).isEqualTo("First auth attempt cancellation completed")
 
@@ -449,6 +455,29 @@
         }
 
     @Test
+    fun multipleCancelCallsShouldNotCauseMultipleCancellationStatusBeingEmitted() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+            val emittedValues by collectValues(underTest.authenticationStatus)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            underTest.cancel()
+            advanceTimeBy(100)
+            underTest.cancel()
+
+            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+            runCurrent()
+            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+            runCurrent()
+
+            assertThat(emittedValues.size).isEqualTo(1)
+            assertThat(emittedValues.first())
+                .isInstanceOf(ErrorFaceAuthenticationStatus::class.java)
+            assertThat((emittedValues.first() as ErrorFaceAuthenticationStatus).msgId).isEqualTo(-1)
+        }
+
+    @Test
     fun faceHelpMessagesAreIgnoredBasedOnConfig() =
         testScope.runTest {
             overrideResource(
@@ -465,7 +494,7 @@
             authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
             authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
 
-            assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+            assertThat(authStatus()).isEqualTo(HelpFaceAuthenticationStatus(9, "help msg"))
         }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 264328b..def016a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -26,7 +26,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -49,7 +53,6 @@
 @RunWith(AndroidJUnit4::class)
 class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
     @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
-    @Mock private lateinit var dumpManager: DumpManager
     @Mock private lateinit var authController: AuthController
     @Captor
     private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@@ -68,7 +71,6 @@
                 authController,
                 keyguardUpdateMonitor,
                 testScope.backgroundScope,
-                dumpManager,
             )
     }
 
@@ -177,4 +179,129 @@
             callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
             assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
         }
+
+    @Test
+    fun onFingerprintSuccess_successAuthenticationStatus() =
+        testScope.runTest {
+            val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            updateMonitorCallback.value.onBiometricAuthenticated(
+                0,
+                BiometricSourceType.FINGERPRINT,
+                true,
+            )
+
+            val status = authenticationStatus as SuccessFingerprintAuthenticationStatus
+            assertThat(status.userId).isEqualTo(0)
+            assertThat(status.isStrongBiometric).isEqualTo(true)
+        }
+
+    @Test
+    fun onFingerprintFailed_failedAuthenticationStatus() =
+        testScope.runTest {
+            val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            updateMonitorCallback.value.onBiometricAuthFailed(
+                BiometricSourceType.FINGERPRINT,
+            )
+
+            assertThat(authenticationStatus)
+                .isInstanceOf(FailFingerprintAuthenticationStatus::class.java)
+        }
+
+    @Test
+    fun onFingerprintError_errorAuthenticationStatus() =
+        testScope.runTest {
+            val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            updateMonitorCallback.value.onBiometricError(
+                1,
+                "test_string",
+                BiometricSourceType.FINGERPRINT,
+            )
+
+            val status = authenticationStatus as ErrorFingerprintAuthenticationStatus
+            assertThat(status.msgId).isEqualTo(1)
+            assertThat(status.msg).isEqualTo("test_string")
+        }
+
+    @Test
+    fun onFingerprintHelp_helpAuthenticationStatus() =
+        testScope.runTest {
+            val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            updateMonitorCallback.value.onBiometricHelp(
+                1,
+                "test_string",
+                BiometricSourceType.FINGERPRINT,
+            )
+
+            val status = authenticationStatus as HelpFingerprintAuthenticationStatus
+            assertThat(status.msgId).isEqualTo(1)
+            assertThat(status.msg).isEqualTo("test_string")
+        }
+
+    @Test
+    fun onFingerprintAcquired_acquiredAuthenticationStatus() =
+        testScope.runTest {
+            val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            updateMonitorCallback.value.onBiometricAcquired(
+                BiometricSourceType.FINGERPRINT,
+                5,
+            )
+
+            val status = authenticationStatus as AcquiredFingerprintAuthenticationStatus
+            assertThat(status.acquiredInfo).isEqualTo(5)
+        }
+
+    @Test
+    fun onFaceCallbacks_fingerprintAuthenticationStatusIsUnchanged() =
+        testScope.runTest {
+            val authenticationStatus by collectLastValue(underTest.authenticationStatus)
+            runCurrent()
+
+            verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
+            updateMonitorCallback.value.onBiometricAuthenticated(
+                0,
+                BiometricSourceType.FACE,
+                true,
+            )
+            assertThat(authenticationStatus).isNull()
+
+            updateMonitorCallback.value.onBiometricAuthFailed(
+                BiometricSourceType.FACE,
+            )
+            assertThat(authenticationStatus).isNull()
+
+            updateMonitorCallback.value.onBiometricHelp(
+                1,
+                "test_string",
+                BiometricSourceType.FACE,
+            )
+            assertThat(authenticationStatus).isNull()
+
+            updateMonitorCallback.value.onBiometricAcquired(
+                BiometricSourceType.FACE,
+                5,
+            )
+            assertThat(authenticationStatus).isNull()
+
+            updateMonitorCallback.value.onBiometricError(
+                1,
+                "test_string",
+                BiometricSourceType.FACE,
+            )
+            assertThat(authenticationStatus).isNull()
+        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index f541815..ba7d349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -31,11 +31,14 @@
 import com.android.systemui.doze.DozeTransitionCallback
 import com.android.systemui.doze.DozeTransitionListener
 import com.android.systemui.dreams.DreamOverlayCallbackController
+import com.android.systemui.keyguard.ScreenLifecycle
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -71,6 +74,7 @@
     @Mock private lateinit var statusBarStateController: StatusBarStateController
     @Mock private lateinit var keyguardStateController: KeyguardStateController
     @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+    @Mock private lateinit var screenLifecycle: ScreenLifecycle
     @Mock private lateinit var biometricUnlockController: BiometricUnlockController
     @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
     @Mock private lateinit var authController: AuthController
@@ -92,6 +96,7 @@
             KeyguardRepositoryImpl(
                 statusBarStateController,
                 wakefulnessLifecycle,
+                screenLifecycle,
                 biometricUnlockController,
                 keyguardStateController,
                 keyguardBypassController,
@@ -160,6 +165,16 @@
         }
 
     @Test
+    fun dozeTimeTick() =
+        testScope.runTest {
+            var dozeTimeTickValue = collectLastValue(underTest.dozeTimeTick)
+            underTest.dozeTimeTick()
+            runCurrent()
+
+            assertThat(dozeTimeTickValue()).isNull()
+        }
+
+    @Test
     fun isKeyguardShowing() =
         testScope.runTest {
             whenever(keyguardStateController.isShowing).thenReturn(false)
@@ -371,6 +386,48 @@
         }
 
     @Test
+    fun screenModel() =
+        testScope.runTest {
+            val values = mutableListOf<ScreenModel>()
+            val job = underTest.screenModel.onEach(values::add).launchIn(this)
+
+            runCurrent()
+            val captor = argumentCaptor<ScreenLifecycle.Observer>()
+            verify(screenLifecycle).addObserver(captor.capture())
+
+            whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_TURNING_ON)
+            captor.value.onScreenTurningOn()
+            runCurrent()
+
+            whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON)
+            captor.value.onScreenTurnedOn()
+            runCurrent()
+
+            whenever(screenLifecycle.getScreenState())
+                .thenReturn(ScreenLifecycle.SCREEN_TURNING_OFF)
+            captor.value.onScreenTurningOff()
+            runCurrent()
+
+            whenever(screenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_OFF)
+            captor.value.onScreenTurnedOff()
+            runCurrent()
+
+            assertThat(values.map { it.state })
+                .isEqualTo(
+                    listOf(
+                        // Initial value will be OFF
+                        ScreenState.SCREEN_OFF,
+                        ScreenState.SCREEN_TURNING_ON,
+                        ScreenState.SCREEN_ON,
+                        ScreenState.SCREEN_TURNING_OFF,
+                        ScreenState.SCREEN_OFF,
+                    )
+                )
+
+            job.cancel()
+        }
+
+    @Test
     fun isUdfpsSupported() =
         testScope.runTest {
             whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt
new file mode 100644
index 0000000..3389fa9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BiometricMessageInteractorTest.kt
@@ -0,0 +1,260 @@
+/*
+ *  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.keyguard.domain.interactor
+
+import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
+import android.hardware.fingerprint.FingerprintManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.biometrics.shared.model.FingerprintSensorType
+import com.android.systemui.biometrics.shared.model.SensorStrength
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BiometricMessageInteractorTest : SysuiTestCase() {
+
+    private lateinit var underTest: BiometricMessageInteractor
+    private lateinit var testScope: TestScope
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+
+    @Mock private lateinit var indicationHelper: IndicationHelper
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        testScope = TestScope()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+        underTest =
+            BiometricMessageInteractor(
+                mContext.resources,
+                fingerprintAuthRepository,
+                fingerprintPropertyRepository,
+                indicationHelper,
+                keyguardUpdateMonitor,
+            )
+    }
+
+    @Test
+    fun fingerprintErrorMessage() =
+        testScope.runTest {
+            val fingerprintErrorMessage by collectLastValue(underTest.fingerprintErrorMessage)
+
+            // GIVEN FINGERPRINT_ERROR_HW_UNAVAILABLE should NOT be suppressed
+            whenever(
+                    indicationHelper.shouldSuppressErrorMsg(
+                        FINGERPRINT,
+                        FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+                    )
+                )
+                .thenReturn(false)
+
+            // WHEN authentication status error is FINGERPRINT_ERROR_HW_UNAVAILABLE
+            fingerprintAuthRepository.setAuthenticationStatus(
+                ErrorFingerprintAuthenticationStatus(
+                    msgId = FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                    msg = "test"
+                )
+            )
+
+            // THEN fingerprintErrorMessage is updated
+            assertThat(fingerprintErrorMessage?.source).isEqualTo(FINGERPRINT)
+            assertThat(fingerprintErrorMessage?.type).isEqualTo(BiometricMessageType.ERROR)
+            assertThat(fingerprintErrorMessage?.id)
+                .isEqualTo(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE)
+            assertThat(fingerprintErrorMessage?.message).isEqualTo("test")
+        }
+
+    @Test
+    fun fingerprintErrorMessage_suppressedError() =
+        testScope.runTest {
+            val fingerprintErrorMessage by collectLastValue(underTest.fingerprintErrorMessage)
+
+            // GIVEN FINGERPRINT_ERROR_HW_UNAVAILABLE should be suppressed
+            whenever(
+                    indicationHelper.shouldSuppressErrorMsg(
+                        FINGERPRINT,
+                        FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE
+                    )
+                )
+                .thenReturn(true)
+
+            // WHEN authentication status error is FINGERPRINT_ERROR_HW_UNAVAILABLE
+            fingerprintAuthRepository.setAuthenticationStatus(
+                ErrorFingerprintAuthenticationStatus(
+                    msgId = FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE,
+                    msg = "test"
+                )
+            )
+
+            // THEN fingerprintErrorMessage isn't update - it's still null
+            assertThat(fingerprintErrorMessage).isNull()
+        }
+
+    @Test
+    fun fingerprintHelpMessage() =
+        testScope.runTest {
+            val fingerprintHelpMessage by collectLastValue(underTest.fingerprintHelpMessage)
+
+            // GIVEN primary auth is NOT required
+            whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true)
+
+            // WHEN authentication status help is FINGERPRINT_ACQUIRED_IMAGER_DIRTY
+            fingerprintAuthRepository.setAuthenticationStatus(
+                HelpFingerprintAuthenticationStatus(
+                    msgId = FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+                    msg = "test"
+                )
+            )
+
+            // THEN fingerprintHelpMessage is updated
+            assertThat(fingerprintHelpMessage?.source).isEqualTo(FINGERPRINT)
+            assertThat(fingerprintHelpMessage?.type).isEqualTo(BiometricMessageType.HELP)
+            assertThat(fingerprintHelpMessage?.id)
+                .isEqualTo(FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY)
+            assertThat(fingerprintHelpMessage?.message).isEqualTo("test")
+        }
+
+    @Test
+    fun fingerprintHelpMessage_primaryAuthRequired() =
+        testScope.runTest {
+            val fingerprintHelpMessage by collectLastValue(underTest.fingerprintHelpMessage)
+
+            // GIVEN primary auth is required
+            whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(false)
+
+            // WHEN authentication status help is FINGERPRINT_ACQUIRED_IMAGER_DIRTY
+            fingerprintAuthRepository.setAuthenticationStatus(
+                HelpFingerprintAuthenticationStatus(
+                    msgId = FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+                    msg = "test"
+                )
+            )
+
+            // THEN fingerprintHelpMessage isn't update - it's still null
+            assertThat(fingerprintHelpMessage).isNull()
+        }
+
+    @Test
+    fun fingerprintFailMessage_nonUdfps() =
+        testScope.runTest {
+            val fingerprintFailMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+            // GIVEN primary auth is NOT required
+            whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true)
+
+            // GIVEN rear fingerprint (not UDFPS)
+            fingerprintPropertyRepository.setProperties(
+                0,
+                SensorStrength.STRONG,
+                FingerprintSensorType.REAR,
+                mapOf()
+            )
+
+            // WHEN authentication status fail
+            fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+            // THEN fingerprintFailMessage is updated
+            assertThat(fingerprintFailMessage?.source).isEqualTo(FINGERPRINT)
+            assertThat(fingerprintFailMessage?.type).isEqualTo(BiometricMessageType.FAIL)
+            assertThat(fingerprintFailMessage?.id)
+                .isEqualTo(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+            assertThat(fingerprintFailMessage?.message)
+                .isEqualTo(
+                    mContext.resources.getString(
+                        com.android.internal.R.string.fingerprint_error_not_match
+                    )
+                )
+        }
+
+    @Test
+    fun fingerprintFailMessage_udfps() =
+        testScope.runTest {
+            val fingerprintFailMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+            // GIVEN primary auth is NOT required
+            whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(true)
+
+            // GIVEN UDFPS
+            fingerprintPropertyRepository.setProperties(
+                0,
+                SensorStrength.STRONG,
+                FingerprintSensorType.UDFPS_OPTICAL,
+                mapOf()
+            )
+
+            // WHEN authentication status fail
+            fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+            // THEN fingerprintFailMessage is updated to udfps message
+            assertThat(fingerprintFailMessage?.source).isEqualTo(FINGERPRINT)
+            assertThat(fingerprintFailMessage?.type).isEqualTo(BiometricMessageType.FAIL)
+            assertThat(fingerprintFailMessage?.id)
+                .isEqualTo(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+            assertThat(fingerprintFailMessage?.message)
+                .isEqualTo(
+                    mContext.resources.getString(
+                        com.android.internal.R.string.fingerprint_udfps_error_not_match
+                    )
+                )
+        }
+
+    @Test
+    fun fingerprintFailedMessage_primaryAuthRequired() =
+        testScope.runTest {
+            val fingerprintFailedMessage by collectLastValue(underTest.fingerprintFailMessage)
+
+            // GIVEN primary auth is required
+            whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+                .thenReturn(false)
+
+            // WHEN authentication status fail
+            fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+            // THEN fingerprintFailedMessage isn't update - it's still null
+            assertThat(fingerprintFailedMessage).isNull()
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index 3e81cd3..ced0a21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -38,10 +38,9 @@
 import com.android.systemui.keyguard.DismissCallbackRegistry
 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
 import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository
-import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.FakeTrustRepository
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.ErrorFaceAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -121,8 +120,8 @@
                     mock(KeyguardStateController::class.java),
                     bouncerRepository,
                     mock(BiometricSettingsRepository::class.java),
-                    FakeDeviceEntryFingerprintAuthRepository(),
                     FakeSystemClock(),
+                    keyguardUpdateMonitor,
                 ),
                 keyguardTransitionInteractor,
                 featureFlags,
@@ -160,7 +159,7 @@
 
             underTest.onDeviceLifted()
 
-            val outputValue = authenticationStatus()!! as ErrorAuthenticationStatus
+            val outputValue = authenticationStatus()!! as ErrorFaceAuthenticationStatus
             assertThat(outputValue.msgId)
                 .isEqualTo(BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT)
             assertThat(outputValue.msg).isEqualTo("Face Unlock unavailable")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
new file mode 100644
index 0000000..6e52d1a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/OccludingAppDeviceEntryInteractorTest.kt
@@ -0,0 +1,299 @@
+/*
+ *  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.keyguard.domain.interactor
+
+import android.content.Context
+import android.content.Intent
+import android.hardware.biometrics.BiometricSourceType
+import android.hardware.fingerprint.FingerprintManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
+import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeTrustRepository
+import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.keyguard.util.IndicationHelper
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.ActivityStarter.OnDismissAction
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+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.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class OccludingAppDeviceEntryInteractorTest : SysuiTestCase() {
+
+    private lateinit var underTest: OccludingAppDeviceEntryInteractor
+    private lateinit var testScope: TestScope
+    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var fingerprintAuthRepository: FakeDeviceEntryFingerprintAuthRepository
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+    private lateinit var configurationRepository: FakeConfigurationRepository
+    private lateinit var featureFlags: FakeFeatureFlags
+    private lateinit var trustRepository: FakeTrustRepository
+
+    @Mock private lateinit var indicationHelper: IndicationHelper
+    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock private lateinit var mockedContext: Context
+    @Mock private lateinit var activityStarter: ActivityStarter
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        testScope = TestScope()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
+        fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+        keyguardRepository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        configurationRepository = FakeConfigurationRepository()
+        featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.FACE_AUTH_REFACTOR, false)
+                set(Flags.DELAY_BOUNCER, false)
+            }
+        trustRepository = FakeTrustRepository()
+        underTest =
+            OccludingAppDeviceEntryInteractor(
+                BiometricMessageInteractor(
+                    mContext.resources,
+                    fingerprintAuthRepository,
+                    fingerprintPropertyRepository,
+                    indicationHelper,
+                    keyguardUpdateMonitor,
+                ),
+                fingerprintAuthRepository,
+                KeyguardInteractor(
+                    keyguardRepository,
+                    commandQueue = mock(),
+                    featureFlags,
+                    bouncerRepository,
+                    configurationRepository,
+                ),
+                PrimaryBouncerInteractor(
+                    bouncerRepository,
+                    primaryBouncerView = mock(),
+                    mainHandler = mock(),
+                    keyguardStateController = mock(),
+                    keyguardSecurityModel = mock(),
+                    primaryBouncerCallbackInteractor = mock(),
+                    falsingCollector = mock(),
+                    dismissCallbackRegistry = mock(),
+                    context,
+                    keyguardUpdateMonitor,
+                    trustRepository,
+                    featureFlags,
+                    testScope.backgroundScope,
+                ),
+                AlternateBouncerInteractor(
+                    statusBarStateController = mock(),
+                    keyguardStateController = mock(),
+                    bouncerRepository,
+                    biometricSettingsRepository,
+                    FakeSystemClock(),
+                    keyguardUpdateMonitor,
+                ),
+                testScope.backgroundScope,
+                mockedContext,
+                activityStarter,
+            )
+    }
+
+    @Test
+    fun fingerprintSuccess_goToHomeScreen() =
+        testScope.runTest {
+            givenOnOccludingApp(true)
+            fingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+            verifyGoToHomeScreen()
+        }
+
+    @Test
+    fun fingerprintSuccess_notOnOccludingApp_doesNotGoToHomeScreen() =
+        testScope.runTest {
+            givenOnOccludingApp(false)
+            fingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            runCurrent()
+            verifyNeverGoToHomeScreen()
+        }
+
+    @Test
+    fun lockout_goToHomeScreenOnDismissAction() =
+        testScope.runTest {
+            givenOnOccludingApp(true)
+            fingerprintAuthRepository.setAuthenticationStatus(
+                ErrorFingerprintAuthenticationStatus(
+                    FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
+                    "lockoutTest"
+                )
+            )
+            runCurrent()
+            verifyGoToHomeScreenOnDismiss()
+        }
+
+    @Test
+    fun lockout_notOnOccludingApp_neverGoToHomeScreen() =
+        testScope.runTest {
+            givenOnOccludingApp(false)
+            fingerprintAuthRepository.setAuthenticationStatus(
+                ErrorFingerprintAuthenticationStatus(
+                    FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
+                    "lockoutTest"
+                )
+            )
+            runCurrent()
+            verifyNeverGoToHomeScreen()
+        }
+
+    @Test
+    fun message_fpFailOnOccludingApp_thenNotOnOccludingApp() =
+        testScope.runTest {
+            val message by collectLastValue(underTest.message)
+
+            givenOnOccludingApp(true)
+            givenPrimaryAuthRequired(false)
+            runCurrent()
+            // WHEN a fp failure come in
+            fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+            // THEN message set to failure
+            assertThat(message?.type).isEqualTo(BiometricMessageType.FAIL)
+
+            // GIVEN fingerprint shouldn't run
+            givenOnOccludingApp(false)
+            runCurrent()
+            // WHEN another fp failure arrives
+            fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+
+            // THEN message set to null
+            assertThat(message).isNull()
+        }
+
+    @Test
+    fun message_fpErrorHelpFailOnOccludingApp() =
+        testScope.runTest {
+            val message by collectLastValue(underTest.message)
+
+            givenOnOccludingApp(true)
+            givenPrimaryAuthRequired(false)
+            runCurrent()
+
+            // ERROR message
+            fingerprintAuthRepository.setAuthenticationStatus(
+                ErrorFingerprintAuthenticationStatus(
+                    FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
+                    "testError",
+                )
+            )
+            assertThat(message?.source).isEqualTo(BiometricSourceType.FINGERPRINT)
+            assertThat(message?.id).isEqualTo(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)
+            assertThat(message?.message).isEqualTo("testError")
+            assertThat(message?.type).isEqualTo(BiometricMessageType.ERROR)
+
+            // HELP message
+            fingerprintAuthRepository.setAuthenticationStatus(
+                HelpFingerprintAuthenticationStatus(
+                    FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL,
+                    "testHelp",
+                )
+            )
+            assertThat(message?.source).isEqualTo(BiometricSourceType.FINGERPRINT)
+            assertThat(message?.id).isEqualTo(FingerprintManager.FINGERPRINT_ACQUIRED_PARTIAL)
+            assertThat(message?.message).isEqualTo("testHelp")
+            assertThat(message?.type).isEqualTo(BiometricMessageType.HELP)
+
+            // FAIL message
+            fingerprintAuthRepository.setAuthenticationStatus(FailFingerprintAuthenticationStatus)
+            assertThat(message?.source).isEqualTo(BiometricSourceType.FINGERPRINT)
+            assertThat(message?.id)
+                .isEqualTo(KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED)
+            assertThat(message?.type).isEqualTo(BiometricMessageType.FAIL)
+        }
+
+    private fun givenOnOccludingApp(isOnOccludingApp: Boolean) {
+        keyguardRepository.setKeyguardOccluded(isOnOccludingApp)
+        keyguardRepository.setKeyguardShowing(isOnOccludingApp)
+        bouncerRepository.setPrimaryShow(!isOnOccludingApp)
+        bouncerRepository.setAlternateVisible(!isOnOccludingApp)
+    }
+
+    private fun givenPrimaryAuthRequired(required: Boolean) {
+        whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+            .thenReturn(!required)
+    }
+
+    private fun verifyGoToHomeScreen() {
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(mockedContext).startActivity(intentCaptor.capture())
+
+        assertThat(intentCaptor.value.hasCategory(Intent.CATEGORY_HOME)).isTrue()
+        assertThat(intentCaptor.value.action).isEqualTo(Intent.ACTION_MAIN)
+    }
+
+    private fun verifyNeverGoToHomeScreen() {
+        verify(mockedContext, never()).startActivity(any())
+        verify(activityStarter, never())
+            .dismissKeyguardThenExecute(any(OnDismissAction::class.java), isNull(), eq(false))
+    }
+
+    private fun verifyGoToHomeScreenOnDismiss() {
+        val onDimissActionCaptor = ArgumentCaptor.forClass(OnDismissAction::class.java)
+        verify(activityStarter)
+            .dismissKeyguardThenExecute(onDimissActionCaptor.capture(), isNull(), eq(false))
+        onDimissActionCaptor.value.onDismiss()
+
+        verifyGoToHomeScreen()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 1baca21..b019a21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -33,6 +33,9 @@
 import com.android.systemui.keyguard.shared.model.WakeSleepReason
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.time.FakeSystemClock
@@ -46,6 +49,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @ExperimentalCoroutinesApi
@@ -63,19 +67,21 @@
     private lateinit var fakeCommandQueue: FakeCommandQueue
     private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var burnInInteractor: BurnInInteractor
+    private lateinit var shadeRepository: FakeShadeRepository
 
     @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+    @Mock private lateinit var dialogManager: SystemUIDialogManager
 
     private lateinit var underTest: UdfpsKeyguardInteractor
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-
         testScope = TestScope()
         configRepository = FakeConfigurationRepository()
         keyguardRepository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
+        shadeRepository = FakeShadeRepository()
         fakeCommandQueue = FakeCommandQueue()
         featureFlags =
             FakeFeatureFlags().apply {
@@ -102,6 +108,8 @@
                     bouncerRepository,
                     configRepository,
                 ),
+                shadeRepository,
+                dialogManager,
             )
     }
 
@@ -142,6 +150,61 @@
             assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
         }
 
+    @Test
+    fun dialogHideAffordances() =
+        testScope.runTest {
+            val dialogHideAffordancesRequest by
+                collectLastValue(underTest.dialogHideAffordancesRequest)
+            runCurrent()
+            val captor = argumentCaptor<SystemUIDialogManager.Listener>()
+            verify(dialogManager).registerListener(captor.capture())
+
+            captor.value.shouldHideAffordances(false)
+            assertThat(dialogHideAffordancesRequest).isEqualTo(false)
+
+            captor.value.shouldHideAffordances(true)
+            assertThat(dialogHideAffordancesRequest).isEqualTo(true)
+
+            captor.value.shouldHideAffordances(false)
+            assertThat(dialogHideAffordancesRequest).isEqualTo(false)
+        }
+
+    @Test
+    fun shadeExpansion_updates() =
+        testScope.runTest {
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+            val shadeExpansion by collectLastValue(underTest.shadeExpansion)
+            assertThat(shadeExpansion).isEqualTo(0f)
+
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
+            assertThat(shadeExpansion).isEqualTo(.5f)
+
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(.7f)
+            assertThat(shadeExpansion).isEqualTo(.7f)
+
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(.22f)
+            assertThat(shadeExpansion).isEqualTo(.22f)
+
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+            assertThat(shadeExpansion).isEqualTo(1f)
+        }
+
+    @Test
+    fun qsProgress_updates() =
+        testScope.runTest {
+            val qsProgress by collectLastValue(underTest.qsProgress)
+            assertThat(qsProgress).isEqualTo(0f)
+
+            shadeRepository.setQsExpansion(.22f)
+            assertThat(qsProgress).isEqualTo(.44f)
+
+            shadeRepository.setQsExpansion(.5f)
+            assertThat(qsProgress).isEqualTo(1f)
+
+            shadeRepository.setQsExpansion(.7f)
+            assertThat(qsProgress).isEqualTo(1f)
+        }
+
     private fun initializeBurnInOffsets() {
         whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
         whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
index 436c09c..b985b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -32,6 +32,8 @@
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -58,7 +60,9 @@
     private lateinit var keyguardRepository: FakeKeyguardRepository
     private lateinit var fakeCommandQueue: FakeCommandQueue
     private lateinit var featureFlags: FakeFeatureFlags
+    private lateinit var shadeRepository: FakeShadeRepository
 
+    @Mock private lateinit var dialogManager: SystemUIDialogManager
     @Mock private lateinit var burnInHelper: BurnInHelperWrapper
 
     @Before
@@ -70,6 +74,7 @@
         keyguardRepository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
         fakeCommandQueue = FakeCommandQueue()
+        shadeRepository = FakeShadeRepository()
         featureFlags =
             FakeFeatureFlags().apply {
                 set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
@@ -93,6 +98,8 @@
                     bouncerRepository,
                     configRepository,
                 ),
+                shadeRepository,
+                dialogManager,
             )
 
         underTest =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
index a30e2a6..0fbcec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -33,6 +33,8 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
 import com.android.systemui.util.time.FakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -60,8 +62,10 @@
     private lateinit var fakeCommandQueue: FakeCommandQueue
     private lateinit var featureFlags: FakeFeatureFlags
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+    private lateinit var shadeRepository: FakeShadeRepository
 
     @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+    @Mock private lateinit var dialogManager: SystemUIDialogManager
 
     @Before
     fun setUp() {
@@ -79,35 +83,39 @@
             }
         bouncerRepository = FakeKeyguardBouncerRepository()
         transitionRepository = FakeKeyguardTransitionRepository()
+        shadeRepository = FakeShadeRepository()
         val transitionInteractor =
             KeyguardTransitionInteractor(
                 transitionRepository,
                 testScope.backgroundScope,
             )
-        val udfpsKeyguardInteractor =
-            UdfpsKeyguardInteractor(
+        val keyguardInteractor =
+            KeyguardInteractor(
+                keyguardRepository,
+                fakeCommandQueue,
+                featureFlags,
+                bouncerRepository,
                 configRepository,
-                BurnInInteractor(
-                    context,
-                    burnInHelper,
-                    testScope.backgroundScope,
-                    configRepository,
-                    FakeSystemClock(),
-                ),
-                KeyguardInteractor(
-                    keyguardRepository,
-                    fakeCommandQueue,
-                    featureFlags,
-                    bouncerRepository,
-                    configRepository,
-                ),
             )
 
         underTest =
             FingerprintViewModel(
                 context,
                 transitionInteractor,
-                udfpsKeyguardInteractor,
+                UdfpsKeyguardInteractor(
+                    configRepository,
+                    BurnInInteractor(
+                        context,
+                        burnInHelper,
+                        testScope.backgroundScope,
+                        configRepository,
+                        FakeSystemClock(),
+                    ),
+                    keyguardInteractor,
+                    shadeRepository,
+                    dialogManager,
+                ),
+                keyguardInteractor,
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
index d58ceee..41ae931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -20,12 +20,27 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.Utils
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.FakeSystemClock
+import com.android.wm.shell.animation.Interpolators
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -35,6 +50,8 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
 /** Tests UDFPS lockscreen view model transitions. */
@@ -48,26 +65,63 @@
     private val alternateBouncerColor =
         Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
 
+    @Mock private lateinit var dialogManager: SystemUIDialogManager
+
     private lateinit var underTest: UdfpsLockscreenViewModel
     private lateinit var testScope: TestScope
     private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+    private lateinit var configRepository: FakeConfigurationRepository
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+    private lateinit var shadeRepository: FakeShadeRepository
+    private lateinit var featureFlags: FakeFeatureFlags
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         testScope = TestScope()
         transitionRepository = FakeKeyguardTransitionRepository()
-        val transitionInteractor =
-            KeyguardTransitionInteractor(
-                transitionRepository,
-                testScope.backgroundScope,
+        configRepository = FakeConfigurationRepository()
+        keyguardRepository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        shadeRepository = FakeShadeRepository()
+        featureFlags =
+            FakeFeatureFlags().apply {
+                set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+                set(Flags.FACE_AUTH_REFACTOR, false)
+            }
+        val keyguardInteractor =
+            KeyguardInteractor(
+                keyguardRepository,
+                commandQueue = mock(),
+                featureFlags,
+                bouncerRepository,
+                configRepository,
             )
+
         underTest =
             UdfpsLockscreenViewModel(
                 context,
                 lockscreenColorResId,
                 alternateBouncerResId,
-                transitionInteractor,
+                KeyguardTransitionInteractor(
+                    transitionRepository,
+                    testScope.backgroundScope,
+                ),
+                UdfpsKeyguardInteractor(
+                    configRepository,
+                    BurnInInteractor(
+                        context,
+                        burnInHelperWrapper = mock(),
+                        testScope.backgroundScope,
+                        configRepository,
+                        FakeSystemClock(),
+                    ),
+                    keyguardInteractor,
+                    shadeRepository,
+                    dialogManager,
+                ),
+                keyguardInteractor,
             )
     }
 
@@ -125,6 +179,7 @@
         testScope.runTest {
             val transition by collectLastValue(underTest.transition)
             val visible by collectLastValue(underTest.visible)
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
 
             // TransitionState.STARTED: lockscreen -> AOD
             transitionRepository.sendTransitionStep(
@@ -176,6 +231,56 @@
         }
 
     @Test
+    fun lockscreenShadeLockedToAod() =
+        testScope.runTest {
+            val transition by collectLastValue(underTest.transition)
+            val visible by collectLastValue(underTest.visible)
+            keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+            // TransitionState.STARTED: lockscreen -> AOD
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    value = 0f,
+                    transitionState = TransitionState.STARTED,
+                    ownerName = "lockscreenToAod",
+                )
+            )
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(0f)
+            assertThat(visible).isFalse()
+
+            // TransitionState.RUNNING: lockscreen -> AOD
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    value = .6f,
+                    transitionState = TransitionState.RUNNING,
+                    ownerName = "lockscreenToAod",
+                )
+            )
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(0f)
+            assertThat(visible).isFalse()
+
+            // TransitionState.FINISHED: lockscreen -> AOD
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.LOCKSCREEN,
+                    to = KeyguardState.AOD,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED,
+                    ownerName = "lockscreenToAod",
+                )
+            )
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(0f)
+            assertThat(visible).isFalse()
+        }
+
+    @Test
     fun aodToLockscreen() =
         testScope.runTest {
             val transition by collectLastValue(underTest.transition)
@@ -235,6 +340,7 @@
         testScope.runTest {
             val transition by collectLastValue(underTest.transition)
             val visible by collectLastValue(underTest.visible)
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
 
             // TransitionState.STARTED: lockscreen -> alternate bouncer
             transitionRepository.sendTransitionStep(
@@ -398,6 +504,7 @@
         testScope.runTest {
             val transition by collectLastValue(underTest.transition)
             val visible by collectLastValue(underTest.visible)
+            keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
 
             // TransitionState.STARTED: lockscreen -> occluded
             transitionRepository.sendTransitionStep(
@@ -502,4 +609,152 @@
             assertThat(transition?.color).isEqualTo(lockscreenColor)
             assertThat(visible).isTrue()
         }
+
+    @Test
+    fun qsProgressChange() =
+        testScope.runTest {
+            val transition by collectLastValue(underTest.transition)
+            val visible by collectLastValue(underTest.visible)
+            givenTransitionToLockscreenFinished()
+
+            // qsExpansion = 0f
+            shadeRepository.setQsExpansion(0f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(1f)
+            assertThat(visible).isEqualTo(true)
+
+            // qsExpansion = .25
+            shadeRepository.setQsExpansion(.2f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(.6f)
+            assertThat(visible).isEqualTo(true)
+
+            // qsExpansion = .5
+            shadeRepository.setQsExpansion(.5f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(0f)
+            assertThat(visible).isEqualTo(false)
+
+            // qsExpansion = 1
+            shadeRepository.setQsExpansion(1f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(0f)
+            assertThat(visible).isEqualTo(false)
+        }
+
+    @Test
+    fun shadeExpansionChanged() =
+        testScope.runTest {
+            val transition by collectLastValue(underTest.transition)
+            val visible by collectLastValue(underTest.visible)
+            givenTransitionToLockscreenFinished()
+
+            // shadeExpansion = 0f
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(0f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(1f)
+            assertThat(visible).isEqualTo(true)
+
+            // shadeExpansion = .2
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(.2f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(.8f)
+            assertThat(visible).isEqualTo(true)
+
+            // shadeExpansion = .5
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(.5f)
+            assertThat(visible).isEqualTo(true)
+
+            // shadeExpansion = 1
+            shadeRepository.setUdfpsTransitionToFullShadeProgress(1f)
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(0f)
+            assertThat(visible).isEqualTo(false)
+        }
+
+    @Test
+    fun dialogHideAffordancesRequestChanged() =
+        testScope.runTest {
+            val transition by collectLastValue(underTest.transition)
+            givenTransitionToLockscreenFinished()
+            runCurrent()
+            val captor = argumentCaptor<SystemUIDialogManager.Listener>()
+            Mockito.verify(dialogManager).registerListener(captor.capture())
+
+            captor.value.shouldHideAffordances(true)
+            assertThat(transition?.alpha).isEqualTo(0f)
+
+            captor.value.shouldHideAffordances(false)
+            assertThat(transition?.alpha).isEqualTo(1f)
+        }
+
+    @Test
+    fun occludedToAlternateBouncer() =
+        testScope.runTest {
+            val transition by collectLastValue(underTest.transition)
+            val visible by collectLastValue(underTest.visible)
+
+            // TransitionState.STARTED: occluded -> alternate bouncer
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.OCCLUDED,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    value = 0f,
+                    transitionState = TransitionState.STARTED,
+                    ownerName = "occludedToAlternateBouncer",
+                )
+            )
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(1f)
+            assertThat(transition?.scale).isEqualTo(0f)
+            assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+            assertThat(visible).isTrue()
+
+            // TransitionState.RUNNING: occluded -> alternate bouncer
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.OCCLUDED,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    value = .6f,
+                    transitionState = TransitionState.RUNNING,
+                    ownerName = "occludedToAlternateBouncer",
+                )
+            )
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(1f)
+            assertThat(transition?.scale)
+                .isEqualTo(Interpolators.FAST_OUT_SLOW_IN.getInterpolation(.6f))
+            assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+            assertThat(visible).isTrue()
+
+            // TransitionState.FINISHED: occluded -> alternate bouncer
+            transitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.OCCLUDED,
+                    to = KeyguardState.ALTERNATE_BOUNCER,
+                    value = 1f,
+                    transitionState = TransitionState.FINISHED,
+                    ownerName = "occludedToAlternateBouncer",
+                )
+            )
+            runCurrent()
+            assertThat(transition?.alpha).isEqualTo(1f)
+            assertThat(transition?.scale).isEqualTo(1f)
+            assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+            assertThat(visible).isTrue()
+        }
+
+    private suspend fun givenTransitionToLockscreenFinished() {
+        transitionRepository.sendTransitionStep(
+            TransitionStep(
+                from = KeyguardState.AOD,
+                to = KeyguardState.LOCKSCREEN,
+                value = 1f,
+                transitionState = TransitionState.FINISHED,
+                ownerName = "givenTransitionToLockscreenFinished",
+            )
+        )
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
new file mode 100644
index 0000000..272d686
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/FakeLogBuffer.kt
@@ -0,0 +1,49 @@
+/*
+ * 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.log.core
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogMessageImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import org.mockito.Mockito.anyString
+
+/**
+ * A fake [LogBuffer] used for testing that obtains a real [LogMessage] to prevent a
+ * [NullPointerException].
+ */
+class FakeLogBuffer private constructor() {
+    class Factory private constructor() {
+        companion object {
+            fun create(): LogBuffer {
+                val logBuffer = mock<LogBuffer>()
+                whenever(
+                        logBuffer.obtain(
+                            tag = anyString(),
+                            level = any(),
+                            messagePrinter = any(),
+                            exception = nullable(),
+                        )
+                    )
+                    .thenReturn(LogMessageImpl.Factory.create())
+                return logBuffer
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index f79c53d..ab24c46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -63,7 +63,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -124,7 +123,7 @@
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
         mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
                 mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
index f8971fd..45e8e27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogTest.java
@@ -70,7 +70,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -126,7 +125,7 @@
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 9f06b5f..708bb55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -93,7 +93,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -197,7 +196,7 @@
         mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
         mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
         when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
@@ -279,7 +278,7 @@
         mMediaOutputController = new MediaOutputController(mSpyContext, null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
         mMediaOutputController.start(mCb);
@@ -309,7 +308,7 @@
         mMediaOutputController = new MediaOutputController(mSpyContext, null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
         mMediaOutputController.start(mCb);
@@ -530,7 +529,7 @@
                 "",
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
         testMediaOutputController.start(mCb);
         reset(mCb);
@@ -553,7 +552,7 @@
                 "",
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
         testMediaOutputController.start(mCb);
         reset(mCb);
@@ -589,7 +588,7 @@
                 null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
         LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
@@ -606,7 +605,7 @@
                 null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
         LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
@@ -783,33 +782,20 @@
     }
 
     @Test
-    public void getActiveRemoteMediaDevice_isSystemSession_returnSession() {
+    public void getActiveRemoteMediaDevices() {
         when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
         when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
         when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100);
         when(mRemoteSessionInfo.getVolume()).thenReturn(10);
         when(mRemoteSessionInfo.isSystemSession()).thenReturn(false);
         mRoutingSessionInfos.add(mRemoteSessionInfo);
-        when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
+        when(mLocalMediaManager.getRemoteRoutingSessions()).thenReturn(mRoutingSessionInfos);
 
         assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).containsExactly(
                 mRemoteSessionInfo);
     }
 
     @Test
-    public void getActiveRemoteMediaDevice_notSystemSession_returnEmpty() {
-        when(mRemoteSessionInfo.getId()).thenReturn(TEST_SESSION_ID);
-        when(mRemoteSessionInfo.getName()).thenReturn(TEST_SESSION_NAME);
-        when(mRemoteSessionInfo.getVolumeMax()).thenReturn(100);
-        when(mRemoteSessionInfo.getVolume()).thenReturn(10);
-        when(mRemoteSessionInfo.isSystemSession()).thenReturn(true);
-        mRoutingSessionInfos.add(mRemoteSessionInfo);
-        when(mLocalMediaManager.getActiveMediaSession()).thenReturn(mRoutingSessionInfos);
-
-        assertThat(mMediaOutputController.getActiveRemoteMediaDevices()).isEmpty();
-    }
-
-    @Test
     public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() {
         final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
         final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
@@ -888,7 +874,7 @@
         mMediaOutputController = new MediaOutputController(mSpyContext, null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
         assertThat(mMediaOutputController.getNotificationIcon()).isNull();
@@ -1080,7 +1066,7 @@
                 null,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
 
         testMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index a14ff2f..3e69a29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -67,7 +67,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Optional;
 import java.util.function.Consumer;
 
 @MediumTest
@@ -132,7 +131,7 @@
         mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
                 mMediaSessionManager, mLocalBluetoothManager, mStarter,
                 mNotifCollection, mDialogLaunchAnimator,
-                Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+                mNearbyMediaDevicesManager, mAudioManager, mPowerExemptionManager,
                 mKeyguardManager, mFlags, mUserTracker);
         mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
         mMediaOutputDialog = makeTestDialog(mMediaOutputController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 01ffdcd..ee3b80a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -26,6 +26,7 @@
 import android.view.WindowManager
 import android.view.WindowMetrics
 import androidx.core.view.WindowInsetsCompat.Type
+import androidx.lifecycle.LifecycleOwner
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener
@@ -41,9 +42,10 @@
 @SmallTest
 class TaskPreviewSizeProviderTest : SysuiTestCase() {
 
-    private val mockContext: Context = mock()
-    private val resources: Resources = mock()
-    private val windowManager: WindowManager = mock()
+    private val lifecycleOwner = mock<LifecycleOwner>()
+    private val mockContext = mock<Context>()
+    private val resources = mock<Resources>()
+    private val windowManager = mock<WindowManager>()
     private val sizeUpdates = arrayListOf<Rect>()
     private val testConfigurationController = FakeConfigurationController()
 
@@ -76,7 +78,7 @@
     @Test
     fun size_phoneDisplayAndRotate_emitsSizeUpdate() {
         givenDisplay(width = 400, height = 600, isTablet = false)
-        createSizeProvider()
+        createSizeProvider().also { it.onCreate(lifecycleOwner) }
 
         givenDisplay(width = 600, height = 400, isTablet = false)
         testConfigurationController.onConfigurationChanged(Configuration())
@@ -87,7 +89,7 @@
     @Test
     fun size_phoneDisplayAndRotateConfigurationChange_returnsUpdatedSize() {
         givenDisplay(width = 400, height = 600, isTablet = false)
-        val sizeProvider = createSizeProvider()
+        val sizeProvider = createSizeProvider().also { it.onCreate(lifecycleOwner) }
 
         givenDisplay(width = 600, height = 400, isTablet = false)
         testConfigurationController.onConfigurationChanged(Configuration())
@@ -95,6 +97,20 @@
         assertThat(sizeProvider.size).isEqualTo(Rect(0, 0, 150, 100))
     }
 
+    @Test
+    fun size_phoneDisplayAndRotateConfigurationChange_afterChooserDestroyed_doesNotUpdate() {
+        givenDisplay(width = 400, height = 600, isTablet = false)
+        val sizeProvider = createSizeProvider()
+        val previousSize = Rect(sizeProvider.size)
+
+        sizeProvider.onCreate(lifecycleOwner)
+        sizeProvider.onDestroy(lifecycleOwner)
+        givenDisplay(width = 600, height = 400, isTablet = false)
+        testConfigurationController.onConfigurationChanged(Configuration())
+
+        assertThat(sizeProvider.size).isEqualTo(previousSize)
+    }
+
     private fun givenTaskbarSize(size: Int) {
         val windowInsets =
             WindowInsets.Builder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 9188293..ecd3308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -370,7 +370,8 @@
                 mScreenOffAnimationController,
                 mKeyguardLogger,
                 mFeatureFlags,
-                mInteractionJankMonitor));
+                mInteractionJankMonitor,
+                mDumpManager));
 
         when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false);
         when(mHeadsUpCallback.getContext()).thenReturn(mContext);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
index ed24947..f91e5a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
@@ -21,7 +21,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -47,14 +46,11 @@
     private lateinit var onBeforeRenderListListener: OnBeforeRenderListListener
     private val keyguardStateController: KeyguardStateController = mock()
     private val pipeline: NotifPipeline = mock()
-    private val flags: NotifPipelineFlags = mock()
     private val dumpManager: DumpManager = mock()
 
     @Before
     fun setUp() {
-        whenever(flags.allowDismissOngoing()).thenReturn(true)
-
-        dismissibilityProvider = NotificationDismissibilityProviderImpl(flags, dumpManager)
+        dismissibilityProvider = NotificationDismissibilityProviderImpl(dumpManager)
         coordinator = DismissibilityCoordinator(keyguardStateController, dismissibilityProvider)
         coordinator.attach(pipeline)
         onBeforeRenderListListener = withArgCaptor {
@@ -309,57 +305,4 @@
             dismissibilityProvider.isDismissable(summary)
         )
     }
-
-    @Test
-    fun testFeatureToggleOffNonDismissibleEntry() {
-        whenever(flags.allowDismissOngoing()).thenReturn(false)
-        val entry =
-            NotificationEntryBuilder()
-                .setTag("entry")
-                .setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
-                .build()
-
-        onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
-        assertTrue(
-            "FLAG_NO_DISMISS should be ignored, if the feature is off",
-            dismissibilityProvider.isDismissable(entry)
-        )
-    }
-
-    @Test
-    fun testFeatureToggleOffOngoingNotifWhenPhoneIsLocked() {
-        whenever(flags.allowDismissOngoing()).thenReturn(false)
-        whenever(keyguardStateController.isUnlocked).thenReturn(false)
-        val entry =
-            NotificationEntryBuilder()
-                .setTag("entry")
-                .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
-                .build()
-
-        onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
-        assertFalse(
-            "Ongoing Notifs should NOT be dismissible, if the feature is off",
-            dismissibilityProvider.isDismissable(entry)
-        )
-    }
-
-    @Test
-    fun testFeatureToggleOffOngoingNotifWhenPhoneIsUnLocked() {
-        whenever(flags.allowDismissOngoing()).thenReturn(false)
-        whenever(keyguardStateController.isUnlocked).thenReturn(true)
-        val entry =
-            NotificationEntryBuilder()
-                .setTag("entry")
-                .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
-                .build()
-
-        onBeforeRenderListListener.onBeforeRenderList(listOf(entry))
-
-        assertFalse(
-            "Ongoing Notifs should NOT be dismissible, if the feature is off",
-            dismissibilityProvider.isDismissable(entry)
-        )
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 608778e..1dc8453 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -87,6 +88,7 @@
 @RunWithLooper
 public class ExpandableNotificationRowTest extends SysuiTestCase {
 
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     private NotificationTestHelper mNotificationTestHelper;
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
 
@@ -96,12 +98,10 @@
         mNotificationTestHelper = new NotificationTestHelper(
                 mContext,
                 mDependency,
-                TestableLooper.get(this));
+                TestableLooper.get(this),
+                mFeatureFlags);
         mNotificationTestHelper.setDefaultInflationFlags(FLAG_CONTENT_VIEW_ALL);
-
-        FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
-        fakeFeatureFlags.set(Flags.SENSITIVE_REVEAL_ANIM, false);
-        mNotificationTestHelper.setFeatureFlags(fakeFeatureFlags);
+        mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
     }
 
     @Test
@@ -183,6 +183,14 @@
     }
 
     @Test
+    public void testSetSensitiveOnNotifRowNotifiesOfHeightChange_withOtherFlagValue()
+            throws Exception {
+        FakeFeatureFlags flags = mFeatureFlags;
+        flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+        testSetSensitiveOnNotifRowNotifiesOfHeightChange();
+    }
+
+    @Test
     public void testSetSensitiveOnNotifRowNotifiesOfHeightChange() throws Exception {
         // GIVEN a sensitive notification row that's currently redacted
         ExpandableNotificationRow row = mNotificationTestHelper.createRow();
@@ -199,10 +207,19 @@
         // WHEN the row is set to no longer be sensitive
         row.setSensitive(false, true);
 
+        boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
         // VERIFY that the height change listener is invoked
         assertThat(row.getShowingLayout()).isSameInstanceAs(row.getPrivateLayout());
         assertThat(row.getIntrinsicHeight()).isGreaterThan(0);
-        verify(listener).onHeightChanged(eq(row), eq(false));
+        verify(listener).onHeightChanged(eq(row), eq(expectAnimation));
+    }
+
+    @Test
+    public void testSetSensitiveOnGroupRowNotifiesOfHeightChange_withOtherFlagValue()
+            throws Exception {
+        FakeFeatureFlags flags = mFeatureFlags;
+        flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+        testSetSensitiveOnGroupRowNotifiesOfHeightChange();
     }
 
     @Test
@@ -222,10 +239,19 @@
         // WHEN the row is set to no longer be sensitive
         group.setSensitive(false, true);
 
+        boolean expectAnimation = mFeatureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM);
         // VERIFY that the height change listener is invoked
         assertThat(group.getShowingLayout()).isSameInstanceAs(group.getPrivateLayout());
         assertThat(group.getIntrinsicHeight()).isGreaterThan(0);
-        verify(listener).onHeightChanged(eq(group), eq(false));
+        verify(listener).onHeightChanged(eq(group), eq(expectAnimation));
+    }
+
+    @Test
+    public void testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange_withOtherFlagValue()
+            throws Exception {
+        FakeFeatureFlags flags = mFeatureFlags;
+        flags.set(Flags.SENSITIVE_REVEAL_ANIM, !flags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM));
+        testSetSensitiveOnPublicRowDoesNotNotifyOfHeightChange();
     }
 
     @Test
@@ -254,7 +280,7 @@
         assertThat(publicRow.getIntrinsicHeight()).isGreaterThan(0);
         assertThat(publicRow.getPrivateLayout().getMinHeight())
                 .isEqualTo(publicRow.getPublicLayout().getMinHeight());
-        verify(listener, never()).onHeightChanged(eq(publicRow), eq(false));
+        verify(listener, never()).onHeightChanged(eq(publicRow), anyBoolean());
     }
 
     private void measureAndLayout(ExpandableNotificationRow row) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1a644d3..d21029d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -48,12 +48,16 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -90,6 +94,7 @@
 
 import org.mockito.ArgumentCaptor;
 
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -130,14 +135,24 @@
     private final NotificationDismissibilityProvider mDismissibilityProvider;
     public final Runnable mFutureDismissalRunnable;
     private @InflationFlag int mDefaultInflationFlags;
-    private FeatureFlags mFeatureFlags;
+    private final FakeFeatureFlags mFeatureFlags;
 
     public NotificationTestHelper(
             Context context,
             TestableDependency dependency,
             TestableLooper testLooper) {
+        this(context, dependency, testLooper, new FakeFeatureFlags());
+    }
+
+    public NotificationTestHelper(
+            Context context,
+            TestableDependency dependency,
+            TestableLooper testLooper,
+            @NonNull FakeFeatureFlags featureFlags) {
         mContext = context;
         mTestLooper = testLooper;
+        mFeatureFlags = Objects.requireNonNull(featureFlags);
+        dependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
         dependency.injectMockDependency(NotificationMediaManager.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
         dependency.injectMockDependency(MediaOutputDialogFactory.class);
@@ -183,17 +198,12 @@
         mFutureDismissalRunnable = mock(Runnable.class);
         when(mOnUserInteractionCallback.registerFutureDismissal(any(), anyInt()))
                 .thenReturn(mFutureDismissalRunnable);
-        mFeatureFlags = mock(FeatureFlags.class);
     }
 
     public void setDefaultInflationFlags(@InflationFlag int defaultInflationFlags) {
         mDefaultInflationFlags = defaultInflationFlags;
     }
 
-    public void setFeatureFlags(FeatureFlags featureFlags) {
-        mFeatureFlags = featureFlags;
-    }
-
     public ExpandableNotificationRowLogger getMockLogger() {
         return mMockLogger;
     }
@@ -527,6 +537,10 @@
             @InflationFlag int extraInflationFlags,
             int importance)
             throws Exception {
+        // NOTE: This flag is read when the ExpandableNotificationRow is inflated, so it needs to be
+        //  set, but we do not want to override an existing value that is needed by a specific test.
+        mFeatureFlags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS);
+
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
         mRow = (ExpandableNotificationRow) inflater.inflate(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
index 09382ec..3d75288 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/AmbientStateTest.kt
@@ -20,7 +20,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -42,7 +41,6 @@
     private val bypassController = StackScrollAlgorithm.BypassController { false }
     private val statusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
     private val largeScreenShadeInterpolator = mock<LargeScreenShadeInterpolator>()
-    private val featureFlags = mock<FeatureFlags>()
 
     private lateinit var sut: AmbientState
 
@@ -55,8 +53,7 @@
                 sectionProvider,
                 bypassController,
                 statusBarKeyguardViewManager,
-                largeScreenShadeInterpolator,
-                featureFlags
+                largeScreenShadeInterpolator
             )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index f38881c..4b145d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -30,7 +30,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.media.controls.ui.KeyguardMediaController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
@@ -65,7 +64,6 @@
     @Mock private SectionHeaderController mPeopleHeaderController;
     @Mock private SectionHeaderController mAlertingHeaderController;
     @Mock private SectionHeaderController mSilentHeaderController;
-    @Mock private FeatureFlags mFeatureFlag;
 
     private NotificationSectionsManager mSectionsManager;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 8d751e3..1dc0ab0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -9,7 +9,9 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ShadeInterpolation
+import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.NotificationShelf
 import com.android.systemui.statusbar.StatusBarIconView
@@ -20,6 +22,7 @@
 import junit.framework.Assert.assertEquals
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
+import org.junit.Assume.assumeTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -34,22 +37,32 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
-class NotificationShelfTest : SysuiTestCase() {
+open class NotificationShelfTest : SysuiTestCase() {
+
+    open val useShelfRefactor: Boolean = false
+    open val useSensitiveReveal: Boolean = false
+    private val flags = FakeFeatureFlags()
 
     @Mock
     private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
     @Mock
-    private lateinit var flags: FeatureFlags
-    @Mock
     private lateinit var ambientState: AmbientState
     @Mock
     private lateinit var hostLayoutController: NotificationStackScrollLayoutController
+    @Mock
+    private lateinit var hostLayout: NotificationStackScrollLayout
+    @Mock
+    private lateinit var roundnessManager: NotificationRoundnessManager
 
     private lateinit var shelf: NotificationShelf
 
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
+        mDependency.injectTestDependency(FeatureFlags::class.java, flags)
+        flags.set(Flags.NOTIFICATION_SHELF_REFACTOR, useShelfRefactor)
+        flags.set(Flags.SENSITIVE_REVEAL_ANIM, useSensitiveReveal)
+        flags.setDefault(Flags.IMPROVED_HUN_ANIMATIONS)
         val root = FrameLayout(context)
         shelf = LayoutInflater.from(root.context)
                 .inflate(/* resource = */ R.layout.status_bar_notification_shelf,
@@ -57,10 +70,13 @@
                     /* attachToRoot = */false) as NotificationShelf
 
         whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator)
-        whenever(ambientState.featureFlags).thenReturn(flags)
         whenever(ambientState.isSmallScreen).thenReturn(true)
 
-        shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController)
+        if (useShelfRefactor) {
+            shelf.bind(ambientState, hostLayout, roundnessManager)
+        } else {
+            shelf.bind(ambientState, hostLayoutController)
+        }
         shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5)
     }
 
@@ -345,7 +361,7 @@
     @Test
     fun updateState_withNullLastVisibleBackgroundChild_hideShelf() {
         // GIVEN
-        shelf.setSensitiveRevealAnimEnabled(true)
+        assumeTrue(useSensitiveReveal)
         whenever(ambientState.stackY).thenReturn(100f)
         whenever(ambientState.stackHeight).thenReturn(100f)
         val paddingBetweenElements =
@@ -372,7 +388,7 @@
     @Test
     fun updateState_withNullFirstViewInShelf_hideShelf() {
         // GIVEN
-        shelf.setSensitiveRevealAnimEnabled(true)
+        assumeTrue(useSensitiveReveal)
         whenever(ambientState.stackY).thenReturn(100f)
         whenever(ambientState.stackHeight).thenReturn(100f)
         val paddingBetweenElements =
@@ -399,7 +415,7 @@
     @Test
     fun updateState_withCollapsedShade_hideShelf() {
         // GIVEN
-        shelf.setSensitiveRevealAnimEnabled(true)
+        assumeTrue(useSensitiveReveal)
         whenever(ambientState.stackY).thenReturn(100f)
         whenever(ambientState.stackHeight).thenReturn(100f)
         val paddingBetweenElements =
@@ -426,7 +442,7 @@
     @Test
     fun updateState_withHiddenSectionBeforeShelf_hideShelf() {
         // GIVEN
-        shelf.setSensitiveRevealAnimEnabled(true)
+        assumeTrue(useSensitiveReveal)
         whenever(ambientState.stackY).thenReturn(100f)
         whenever(ambientState.stackHeight).thenReturn(100f)
         val paddingBetweenElements =
@@ -486,3 +502,25 @@
         assertEquals(expectedAlpha, shelf.viewState.alpha)
     }
 }
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithRefactorTest : NotificationShelfTest() {
+    override val useShelfRefactor: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithSensitiveRevealTest : NotificationShelfTest() {
+    override val useSensitiveReveal: Boolean = true
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper
+class NotificationShelfWithBothFlagsTest : NotificationShelfTest() {
+    override val useShelfRefactor: Boolean = true
+    override val useSensitiveReveal: Boolean = true
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index ee8325e..07eadf7c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -54,7 +54,6 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -119,6 +118,7 @@
 @RunWith(AndroidTestingRunner.class)
 public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase {
 
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     @Mock private NotificationGutsManager mNotificationGutsManager;
     @Mock private NotificationsController mNotificationsController;
     @Mock private NotificationVisibilityProvider mVisibilityProvider;
@@ -157,7 +157,6 @@
     @Mock private StackStateLogger mStackLogger;
     @Mock private NotificationStackScrollLogger mLogger;
     @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
-    private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     @Mock private NotificationTargetsHelper mNotificationTargetsHelper;
     @Mock private SecureSettings mSecureSettings;
     @Mock private NotificationIconAreaController mIconAreaController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8ad271b..72fcdec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -68,7 +68,9 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
 import com.android.systemui.shade.ShadeController;
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
 import com.android.systemui.statusbar.EmptyShadeView;
@@ -106,6 +108,7 @@
 @TestableLooper.RunWithLooper
 public class NotificationStackScrollLayoutTest extends SysuiTestCase {
 
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
     private NotificationStackScrollLayout mStackScroller;  // Normally test this
     private NotificationStackScrollLayout mStackScrollerInternal;  // See explanation below
     private AmbientState mAmbientState;
@@ -129,7 +132,6 @@
     @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
-    @Mock private FeatureFlags mFeatureFlags;
 
     @Before
     public void setUp() throws Exception {
@@ -143,11 +145,25 @@
                 mNotificationSectionsManager,
                 mBypassController,
                 mStatusBarKeyguardViewManager,
-                mLargeScreenShadeInterpolator,
-                mFeatureFlags
+                mLargeScreenShadeInterpolator
         ));
 
+        // Register the debug flags we use
+        assertFalse(Flags.NSSL_DEBUG_LINES.getDefault());
+        assertFalse(Flags.NSSL_DEBUG_REMOVE_ANIMATION.getDefault());
+        mFeatureFlags.set(Flags.NSSL_DEBUG_LINES, false);
+        mFeatureFlags.set(Flags.NSSL_DEBUG_REMOVE_ANIMATION, false);
+
+        // Register the feature flags we use
+        // TODO: Ideally we wouldn't need to set these unless a test actually reads them,
+        //  and then we would test both configurations, but currently they are all read
+        //  in the constructor.
+        mFeatureFlags.setDefault(Flags.SENSITIVE_REVEAL_ANIM);
+        mFeatureFlags.setDefault(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS);
+        mFeatureFlags.setDefault(Flags.NOTIFICATION_SHELF_REFACTOR);
+
         // Inject dependencies before initializing the layout
+        mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
         mDependency.injectTestDependency(SysuiStatusBarStateController.class, mBarState);
         mDependency.injectMockDependency(ShadeController.class);
         mDependency.injectTestDependency(
@@ -176,13 +192,18 @@
         mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper,
                 mNotificationStackSizeCalculator);
         mStackScroller = spy(mStackScrollerInternal);
-        mStackScroller.setShelfController(notificationShelfController);
+        if (!mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+            mStackScroller.setShelfController(notificationShelfController);
+        }
         mStackScroller.setNotificationsController(mNotificationsController);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
         when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
         when(mStackScrollLayoutController.getNotificationRoundnessManager())
                 .thenReturn(mNotificationRoundnessManager);
         mStackScroller.setController(mStackScrollLayoutController);
+        if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
+            mStackScroller.setShelf(mNotificationShelf);
+        }
 
         doNothing().when(mGroupExpansionManager).collapseGroups();
         doNothing().when(mExpandHelper).cancelImmediately();
@@ -899,7 +920,6 @@
     @Test
     public void testWindowInsetAnimationProgress_updatesBottomInset() {
         int bottomImeInset = 100;
-        mStackScrollerInternal.setAnimatedInsetsEnabled(true);
         WindowInsets windowInsets = new WindowInsets.Builder()
                 .setInsets(ime(), Insets.of(0, 0, 0, bottomImeInset)).build();
         ArrayList<WindowInsetsAnimation> windowInsetsAnimations = new ArrayList<>();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index df65c09..85a2bdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -47,6 +47,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -83,7 +84,7 @@
     private Handler mHandler;
     private ExpandableNotificationRow mNotificationRow;
     private Runnable mFalsingCheck;
-    private FeatureFlags mFeatureFlags;
+    private final FeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
     private static final int FAKE_ROW_WIDTH = 20;
     private static final int FAKE_ROW_HEIGHT = 20;
@@ -96,7 +97,6 @@
         mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
         mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
         mNotificationRoundnessManager = mock(NotificationRoundnessManager.class);
-        mFeatureFlags = mock(FeatureFlags.class);
         mSwipeHelper = spy(new NotificationSwipeHelper(
                 mContext.getResources(),
                 ViewConfiguration.get(mContext),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
index 45725ce..e30947c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelperTest.kt
@@ -18,6 +18,7 @@
 @RunWith(AndroidTestingRunner::class)
 @RunWithLooper
 class NotificationTargetsHelperTest : SysuiTestCase() {
+    private val featureFlags = FakeFeatureFlags()
     lateinit var notificationTestHelper: NotificationTestHelper
     private val sectionsManager: NotificationSectionsManager = mock()
     private val stackScrollLayout: NotificationStackScrollLayout = mock()
@@ -26,10 +27,10 @@
     fun setUp() {
         allowTestableLooperAsMainThread()
         notificationTestHelper =
-            NotificationTestHelper(mContext, mDependency, TestableLooper.get(this))
+            NotificationTestHelper(mContext, mDependency, TestableLooper.get(this), featureFlags)
     }
 
-    private fun notificationTargetsHelper() = NotificationTargetsHelper(FakeFeatureFlags())
+    private fun notificationTargetsHelper() = NotificationTargetsHelper(featureFlags)
 
     @Test
     fun targetsForFirstNotificationInGroup() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 4c97d20..987861d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -8,7 +8,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.shade.transition.LargeScreenShadeInterpolator
 import com.android.systemui.statusbar.EmptyShadeView
 import com.android.systemui.statusbar.NotificationShelf
@@ -45,7 +44,6 @@
     private val dumpManager = mock<DumpManager>()
     private val mStatusBarKeyguardViewManager = mock<StatusBarKeyguardViewManager>()
     private val notificationShelf = mock<NotificationShelf>()
-    private val featureFlags = mock<FeatureFlags>()
     private val emptyShadeView = EmptyShadeView(context, /* attrs= */ null).apply {
         layout(/* l= */ 0, /* t= */ 0, /* r= */ 100, /* b= */ 100)
     }
@@ -56,7 +54,6 @@
             /* bypassController */ { false },
             mStatusBarKeyguardViewManager,
             largeScreenShadeInterpolator,
-            featureFlags,
         )
 
     private val testableResources = mContext.getOrCreateTestableResources()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
index 85fbef0..9795b9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt
@@ -18,6 +18,7 @@
 
 import android.app.AlarmManager
 import android.app.admin.DevicePolicyManager
+import android.app.admin.DevicePolicyResourcesManager
 import android.content.SharedPreferences
 import android.os.UserManager
 import android.telecom.TelecomManager
@@ -49,6 +50,7 @@
 import com.android.systemui.util.RingerModeTracker
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.time.DateFormatUtil
 import com.android.systemui.util.time.FakeSystemClock
@@ -67,6 +69,7 @@
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.inOrder
 import org.mockito.Mockito.never
@@ -83,6 +86,7 @@
     companion object {
         private const val ALARM_SLOT = "alarm"
         private const val CONNECTED_DISPLAY_SLOT = "connected_display"
+        private const val MANAGED_PROFILE_SLOT = "managed_profile"
     }
 
     @Mock private lateinit var iconController: StatusBarIconController
@@ -104,6 +108,7 @@
     @Mock private lateinit var userManager: UserManager
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
+    @Mock private lateinit var devicePolicyManagerResources: DevicePolicyResourcesManager
     @Mock private lateinit var recordingController: RecordingController
     @Mock private lateinit var telecomManager: TelecomManager
     @Mock private lateinit var sharedPreferences: SharedPreferences
@@ -132,6 +137,12 @@
             com.android.internal.R.string.status_bar_alarm_clock,
             ALARM_SLOT
         )
+        context.orCreateTestableResources.addOverride(
+            com.android.internal.R.string.status_bar_managed_profile,
+            MANAGED_PROFILE_SLOT
+        )
+        whenever(devicePolicyManager.resources).thenReturn(devicePolicyManagerResources)
+        whenever(devicePolicyManagerResources.getString(anyString(), any())).thenReturn("")
         statusBarPolicy = createStatusBarPolicy()
     }
 
@@ -182,6 +193,32 @@
     }
 
     @Test
+    fun testAppTransitionFinished_doesNotShowManagedProfileIcon() {
+        whenever(userManager.getUserStatusBarIconResId(anyInt())).thenReturn(0 /* ID_NULL */)
+        whenever(keyguardStateController.isShowing).thenReturn(false)
+        statusBarPolicy.appTransitionFinished(0)
+        // The above call posts to bgExecutor and then back to mainExecutor
+        executor.advanceClockToLast()
+        executor.runAllReady()
+        executor.advanceClockToLast()
+        executor.runAllReady()
+        verify(iconController, never()).setIconVisibility(MANAGED_PROFILE_SLOT, true)
+    }
+
+    @Test
+    fun testAppTransitionFinished_showsManagedProfileIcon() {
+        whenever(userManager.getUserStatusBarIconResId(anyInt())).thenReturn(100)
+        whenever(keyguardStateController.isShowing).thenReturn(false)
+        statusBarPolicy.appTransitionFinished(0)
+        // The above call posts to bgExecutor and then back to mainExecutor
+        executor.advanceClockToLast()
+        executor.runAllReady()
+        executor.advanceClockToLast()
+        executor.runAllReady()
+        verify(iconController).setIconVisibility(MANAGED_PROFILE_SLOT, true)
+    }
+
+    @Test
     fun connectedDisplay_connected_iconShown() =
         testScope.runTest {
             statusBarPolicy.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 9157cd9..085ec27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -15,7 +15,6 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
-import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
 
 import static junit.framework.Assert.assertTrue;
 
@@ -39,14 +38,13 @@
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.StatusBarMobileView;
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
 import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.tuner.TunerService;
@@ -57,23 +55,27 @@
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class StatusBarIconControllerTest extends LeakCheckedTest {
 
     private MobileContextProvider mMobileContextProvider = mock(MobileContextProvider.class);
+    private MobileUiAdapter mMobileUiAdapter = mock(MobileUiAdapter.class);
+    private MobileIconsViewModel mMobileIconsViewModel = mock(MobileIconsViewModel.class);
 
     @Before
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         // For testing, ignore context overrides
         when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
+        when(mMobileUiAdapter.getMobileIconsViewModel()).thenReturn(mMobileIconsViewModel);
     }
 
     @Test
     public void testSetCalledOnAdd_IconManager() {
         LinearLayout layout = new LinearLayout(mContext);
-        TestIconManager manager = new TestIconManager(layout, mMobileContextProvider);
+        TestIconManager manager =
+                new TestIconManager(layout, mMobileUiAdapter, mMobileContextProvider);
         testCallOnAdd_forManager(manager);
     }
 
@@ -83,9 +85,8 @@
         TestDarkIconManager manager = new TestDarkIconManager(
                 layout,
                 StatusBarLocation.HOME,
-                mock(StatusBarPipelineFlags.class),
                 mock(WifiUiAdapter.class),
-                mock(MobileUiAdapter.class),
+                mMobileUiAdapter,
                 mMobileContextProvider,
                 mock(DarkIconDispatcher.class));
         testCallOnAdd_forManager(manager);
@@ -153,15 +154,10 @@
         assertTrue("Expected StatusBarIconView",
                 (manager.getViewAt(0) instanceof StatusBarIconView));
 
-        holder = holderForType(TYPE_MOBILE);
-        manager.onIconAdded(1, "test_mobile", false, holder);
-        assertTrue(manager.getViewAt(1) instanceof StatusBarMobileView);
     }
 
     private StatusBarIconHolder holderForType(int type) {
         switch (type) {
-            case TYPE_MOBILE:
-                return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class));
 
             case TYPE_ICON:
             default:
@@ -175,14 +171,12 @@
         TestDarkIconManager(
                 LinearLayout group,
                 StatusBarLocation location,
-                StatusBarPipelineFlags statusBarPipelineFlags,
                 WifiUiAdapter wifiUiAdapter,
                 MobileUiAdapter mobileUiAdapter,
                 MobileContextProvider contextProvider,
                 DarkIconDispatcher darkIconDispatcher) {
             super(group,
                     location,
-                    statusBarPipelineFlags,
                     wifiUiAdapter,
                     mobileUiAdapter,
                     contextProvider,
@@ -202,23 +196,18 @@
 
             return mock;
         }
-
-        @Override
-        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
-            StatusBarMobileView mock = mock(StatusBarMobileView.class);
-            mGroup.addView(mock, index);
-
-            return mock;
-        }
     }
 
     private static class TestIconManager extends IconManager implements TestableIconManager {
-        TestIconManager(ViewGroup group, MobileContextProvider contextProvider) {
+        TestIconManager(
+                ViewGroup group,
+                MobileUiAdapter adapter,
+                MobileContextProvider contextProvider
+        ) {
             super(group,
                     StatusBarLocation.HOME,
-                    mock(StatusBarPipelineFlags.class),
                     mock(WifiUiAdapter.class),
-                    mock(MobileUiAdapter.class),
+                    adapter,
                     contextProvider);
         }
 
@@ -235,14 +224,6 @@
 
             return mock;
         }
-
-        @Override
-        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
-            StatusBarMobileView mock = mock(StatusBarMobileView.class);
-            mGroup.addView(mock, index);
-
-            return mock;
-        }
     }
 
     private interface TestableIconManager {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 9c7f619..33144f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -64,7 +64,7 @@
 import com.android.systemui.animation.ActivityLaunchAnimator;
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.classifier.FalsingCollectorFake;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
@@ -117,6 +117,7 @@
 public class StatusBarNotificationActivityStarterTest extends SysuiTestCase {
 
     private static final int DISPLAY_ID = 0;
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
     @Mock
     private AssistManager mAssistManager;
@@ -256,7 +257,7 @@
                         notificationAnimationProvider,
                         mock(LaunchFullScreenIntentProvider.class),
                         mPowerInteractor,
-                        mock(FeatureFlags.class),
+                        mFeatureFlags,
                         mUserTracker
                 );
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index cd8aaa2..9c52788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -79,7 +79,6 @@
     private CommandQueue mCommandQueue;
     private FakeMetricsLogger mMetricsLogger;
     private final ShadeController mShadeController = mock(ShadeController.class);
-    private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
     private final NotificationsInteractor mNotificationsInteractor =
             mock(NotificationsInteractor.class);
     private final KeyguardStateController mKeyguardStateController =
@@ -118,7 +117,6 @@
                 mock(NotificationShadeWindowController.class),
                 mock(DynamicPrivacyController.class),
                 mKeyguardStateController,
-                mCentralSurfaces,
                 mNotificationsInteractor,
                 mock(LockscreenShadeTransitionController.class),
                 mock(PowerInteractor.class),
@@ -202,7 +200,6 @@
 
         when(mKeyguardStateController.isShowing()).thenReturn(true);
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
-        when(mCentralSurfaces.isOccluded()).thenReturn(false);
         assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
index 4aa48d6..755aaa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLoggerTest.kt
@@ -54,7 +54,7 @@
     @Test
     fun collectionStarted_dumpHasInfo() {
         val view = TextView(context)
-        val viewModel = QsMobileIconViewModel(commonViewModel, flags)
+        val viewModel = QsMobileIconViewModel(commonViewModel)
 
         underTest.logCollectionStarted(view, viewModel)
 
@@ -66,8 +66,8 @@
     fun collectionStarted_multipleViews_dumpHasInfo() {
         val view = TextView(context)
         val view2 = TextView(context)
-        val viewModel = QsMobileIconViewModel(commonViewModel, flags)
-        val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+        val viewModel = QsMobileIconViewModel(commonViewModel)
+        val viewModel2 = KeyguardMobileIconViewModel(commonViewModel)
 
         underTest.logCollectionStarted(view, viewModel)
         underTest.logCollectionStarted(view2, viewModel2)
@@ -81,8 +81,8 @@
     fun collectionStopped_dumpHasInfo() {
         val view = TextView(context)
         val view2 = TextView(context)
-        val viewModel = QsMobileIconViewModel(commonViewModel, flags)
-        val viewModel2 = KeyguardMobileIconViewModel(commonViewModel, flags)
+        val viewModel = QsMobileIconViewModel(commonViewModel)
+        val viewModel2 = KeyguardMobileIconViewModel(commonViewModel)
 
         underTest.logCollectionStarted(view, viewModel)
         underTest.logCollectionStarted(view2, viewModel2)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
index 7420db2..59fc0ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt
@@ -235,7 +235,6 @@
 
     @Test
     fun onDarkChanged_iconHasNewColor() {
-        whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
         val view =
             ModernStatusBarMobileView.constructAndBind(
                 context,
@@ -257,7 +256,6 @@
 
     @Test
     fun setStaticDrawableColor_iconHasNewColor() {
-        whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
         val view =
             ModernStatusBarMobileView.constructAndBind(
                 context,
@@ -298,7 +296,7 @@
                 constants,
                 testScope.backgroundScope,
             )
-        viewModel = QsMobileIconViewModel(viewModelCommon, statusBarPipelineFlags)
+        viewModel = QsMobileIconViewModel(viewModelCommon)
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
index d5fb577..e59d90f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelTest.kt
@@ -86,9 +86,9 @@
                 testScope.backgroundScope,
             )
 
-        homeIcon = HomeMobileIconViewModel(commonImpl, statusBarPipelineFlags, mock())
-        qsIcon = QsMobileIconViewModel(commonImpl, statusBarPipelineFlags)
-        keyguardIcon = KeyguardMobileIconViewModel(commonImpl, statusBarPipelineFlags)
+        homeIcon = HomeMobileIconViewModel(commonImpl, mock())
+        qsIcon = QsMobileIconViewModel(commonImpl)
+        keyguardIcon = KeyguardMobileIconViewModel(commonImpl)
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 0d51af2..3f49935 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -31,7 +31,6 @@
 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN
 import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
 import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,7 +45,6 @@
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel.Companion.viewModelForLocation
 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
-import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
@@ -64,7 +62,6 @@
 
     private lateinit var testableLooper: TestableLooper
 
-    @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -110,7 +107,6 @@
         viewModel =
             viewModelForLocation(
                 viewModelCommon,
-                statusBarPipelineFlags,
                 StatusBarLocation.HOME,
             )
     }
@@ -199,7 +195,6 @@
 
     @Test
     fun onDarkChanged_iconHasNewColor() {
-        whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
         val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
@@ -215,7 +210,6 @@
 
     @Test
     fun setStaticDrawableColor_iconHasNewColor() {
-        whenever(statusBarPipelineFlags.useDebugColoring()).thenReturn(false)
         val view = ModernStatusBarWifiView.constructAndBind(context, SLOT_NAME, viewModel)
         ViewUtils.attachView(view)
         testableLooper.processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 0e303b2..cb469ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -58,7 +57,6 @@
 
     private lateinit var underTest: WifiViewModel
 
-    @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -107,11 +105,9 @@
     @Test
     fun wifiIcon_allLocationViewModelsReceiveSameData() =
         runBlocking(IMMEDIATE) {
-            val home =
-                viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.HOME)
-            val keyguard =
-                viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.KEYGUARD)
-            val qs = viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.QS)
+            val home = viewModelForLocation(underTest, StatusBarLocation.HOME)
+            val keyguard = viewModelForLocation(underTest, StatusBarLocation.KEYGUARD)
+            val qs = viewModelForLocation(underTest, StatusBarLocation.QS)
 
             var latestHome: WifiIcon? = null
             val jobHome = home.wifiIcon.onEach { latestHome = it }.launchIn(this)
@@ -249,11 +245,9 @@
             createAndSetViewModel()
             wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
 
-            val home =
-                viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.HOME)
-            val keyguard =
-                viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.KEYGUARD)
-            val qs = viewModelForLocation(underTest, statusBarPipelineFlags, StatusBarLocation.QS)
+            val home = viewModelForLocation(underTest, StatusBarLocation.HOME)
+            val keyguard = viewModelForLocation(underTest, StatusBarLocation.KEYGUARD)
+            val qs = viewModelForLocation(underTest, StatusBarLocation.QS)
 
             var latestHome: Boolean? = null
             val jobHome = home.isActivityInViewVisible.onEach { latestHome = it }.launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 391c8ca..7c285b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,6 +102,8 @@
             "com.android.sysuitest.dummynotificationsender";
     private static final int DUMMY_MESSAGE_APP_ID = Process.LAST_APPLICATION_UID - 1;
 
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+
     @Mock private RemoteInputController mController;
     @Mock private ShortcutManager mShortcutManager;
     @Mock private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
@@ -453,8 +455,7 @@
     private RemoteInputViewController bindController(
             RemoteInputView view,
             NotificationEntry entry) {
-        FakeFeatureFlags fakeFeatureFlags = new FakeFeatureFlags();
-        fakeFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
+        mFeatureFlags.set(Flags.NOTIFICATION_INLINE_REPLY_ANIMATION, true);
         RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
                 view,
                 entry,
@@ -462,7 +463,7 @@
                 mController,
                 mShortcutManager,
                 mUiEventLoggerFake,
-                fakeFeatureFlags
+                mFeatureFlags
                 );
         viewController.bind();
         return viewController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
index d8e418a..b13cb72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
@@ -26,6 +26,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -56,6 +57,7 @@
     private lateinit var viewRootImpl: ViewRootImpl
     @Mock
     private lateinit var windowToken: IBinder
+    private val wallpaperRepository = FakeWallpaperRepository()
 
     @JvmField
     @Rule
@@ -69,7 +71,7 @@
         `when`(root.windowToken).thenReturn(windowToken)
         `when`(root.isAttachedToWindow).thenReturn(true)
 
-        wallaperController = WallpaperController(wallpaperManager)
+        wallaperController = WallpaperController(wallpaperManager, wallpaperRepository)
 
         wallaperController.rootView = root
     }
@@ -90,9 +92,9 @@
 
     @Test
     fun setUnfoldTransitionZoom_defaultUnfoldTransitionIsDisabled_doesNotUpdateWallpaperZoom() {
-        wallaperController.onWallpaperInfoUpdated(createWallpaperInfo(
+        wallpaperRepository.wallpaperInfo.value = createWallpaperInfo(
             useDefaultTransition = false
-        ))
+        )
 
         wallaperController.setUnfoldTransitionZoom(0.5f)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
index 6fc36b0..fe5024f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
@@ -16,9 +16,11 @@
 
 package com.android.systemui.wallpapers.data.repository
 
+import android.app.WallpaperInfo
 import kotlinx.coroutines.flow.MutableStateFlow
 
 /** Fake implementation of the wallpaper repository. */
 class FakeWallpaperRepository : WallpaperRepository {
+    override val wallpaperInfo = MutableStateFlow<WallpaperInfo?>(null)
     override val wallpaperSupportsAmbientMode = MutableStateFlow(false)
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
index 132b9b4..f8b096a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt
@@ -65,6 +65,171 @@
     }
 
     @Test
+    fun wallpaperInfo_nullInfo() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wallpaperInfo)
+
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null)
+
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun wallpaperInfo_hasInfoFromManager() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wallpaperInfo)
+
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
+
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+
+            assertThat(latest).isEqualTo(UNSUPPORTED_WP)
+        }
+
+    @Test
+    fun wallpaperInfo_initialValueIsFetched() =
+        testScope.runTest {
+            whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id))
+                .thenReturn(SUPPORTED_WP)
+            userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP))
+            userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP)
+
+            // WHEN the repo initially starts up (underTest is lazy), then it fetches the current
+            // value for the wallpaper
+            assertThat(underTest.wallpaperInfo.value).isEqualTo(SUPPORTED_WP)
+        }
+
+    @Test
+    fun wallpaperInfo_updatesOnUserChanged() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wallpaperInfo)
+
+            val user3 = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+            val user3Wp = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(user3.id)).thenReturn(user3Wp)
+
+            val user4 = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+            val user4Wp = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(user4.id)).thenReturn(user4Wp)
+
+            userRepository.setUserInfos(listOf(user3, user4))
+
+            // WHEN user3 is selected
+            userRepository.setSelectedUserInfo(user3)
+
+            // THEN user3's wallpaper is used
+            assertThat(latest).isEqualTo(user3Wp)
+
+            // WHEN the user is switched to user4
+            userRepository.setSelectedUserInfo(user4)
+
+            // THEN user4's wallpaper is used
+            assertThat(latest).isEqualTo(user4Wp)
+        }
+
+    @Test
+    fun wallpaperInfo_doesNotUpdateOnUserChanging() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wallpaperInfo)
+
+            val user3 = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0)
+            val user3Wp = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(user3.id)).thenReturn(user3Wp)
+
+            val user4 = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0)
+            val user4Wp = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(user4.id)).thenReturn(user4Wp)
+
+            userRepository.setUserInfos(listOf(user3, user4))
+
+            // WHEN user3 is selected
+            userRepository.setSelectedUserInfo(user3)
+
+            // THEN user3's wallpaper is used
+            assertThat(latest).isEqualTo(user3Wp)
+
+            // WHEN the user has started switching to user4 but hasn't finished yet
+            userRepository.selectedUser.value =
+                SelectedUserModel(user4, SelectionStatus.SELECTION_IN_PROGRESS)
+
+            // THEN the wallpaper still matches user3
+            assertThat(latest).isEqualTo(user3Wp)
+        }
+
+    @Test
+    fun wallpaperInfo_updatesOnIntent() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.wallpaperInfo)
+
+            val wp1 = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+
+            assertThat(latest).isEqualTo(wp1)
+
+            // WHEN the info is new and a broadcast is sent
+            val wp2 = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp2)
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+
+            // THEN the flow updates
+            assertThat(latest).isEqualTo(wp2)
+        }
+
+    @Test
+    fun wallpaperInfo_wallpaperNotSupported_alwaysNull() =
+        testScope.runTest {
+            whenever(wallpaperManager.isWallpaperSupported).thenReturn(false)
+
+            val latest by collectLastValue(underTest.wallpaperInfo)
+            assertThat(latest).isNull()
+
+            // Even WHEN there *is* current wallpaper
+            val wp1 = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+
+            // THEN the value is still null because wallpaper isn't supported
+            assertThat(latest).isNull()
+        }
+
+    @Test
+    fun wallpaperInfo_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() =
+        testScope.runTest {
+            context.orCreateTestableResources.addOverride(
+                com.android.internal.R.bool.config_dozeSupportsAodWallpaper,
+                false
+            )
+
+            val latest by collectLastValue(underTest.wallpaperInfo)
+            assertThat(latest).isNull()
+
+            // Even WHEN there *is* current wallpaper
+            val wp1 = mock<WallpaperInfo>()
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(wp1)
+            fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+                context,
+                Intent(Intent.ACTION_WALLPAPER_CHANGED),
+            )
+
+            // THEN the value is still null because wallpaper isn't supported
+            assertThat(latest).isNull()
+        }
+
+    @Test
     fun wallpaperSupportsAmbientMode_nullInfo_false() =
         testScope.runTest {
             val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
@@ -190,14 +355,12 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode)
 
-            val info: WallpaperInfo = mock()
-            whenever(info.supportsAmbientMode()).thenReturn(false)
-            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(info)
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP)
 
             assertThat(latest).isFalse()
 
             // WHEN the info now supports ambient mode and a broadcast is sent
-            whenever(info.supportsAmbientMode()).thenReturn(true)
+            whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP)
             fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
                 context,
                 Intent(Intent.ACTION_WALLPAPER_CHANGED),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 4839eeb..820e2a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -92,7 +92,7 @@
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -305,6 +305,7 @@
     private TestableLooper mTestableLooper;
 
     private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
+    private final FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
 
     private UserHandle mUser0;
 
@@ -423,7 +424,7 @@
                 mCommonNotifCollection,
                 mNotifPipeline,
                 mSysUiState,
-                mock(FeatureFlags.class),
+                mFeatureFlags,
                 mNotifPipelineFlags,
                 syncExecutor);
         mBubblesManager.addNotifCallback(mNotifCallback);
@@ -432,7 +433,8 @@
         mNotificationTestHelper = new NotificationTestHelper(
                 mContext,
                 mDependency,
-                TestableLooper.get(this));
+                TestableLooper.get(this),
+                mFeatureFlags);
         mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
         mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
         mNonBubbleNotifRow = mNotificationTestHelper.createRow();
@@ -1844,8 +1846,7 @@
     }
 
     @Test
-    public void testCreateBubbleFromOngoingNotification_OngoingDismissalEnabled() {
-        when(mNotifPipelineFlags.allowDismissOngoing()).thenReturn(true);
+    public void testCreateBubbleFromOngoingNotification() {
         NotificationEntry notif = new NotificationEntryBuilder()
                 .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
                 .setCanBubble(true)
@@ -1858,8 +1859,7 @@
 
 
     @Test
-    public void testCreateBubbleFromNoDismissNotification_OngoingDismissalEnabled() {
-        when(mNotifPipelineFlags.allowDismissOngoing()).thenReturn(true);
+    public void testCreateBubbleFromNoDismissNotification() {
         NotificationEntry notif = new NotificationEntryBuilder()
                 .setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
                 .setCanBubble(true)
@@ -1871,37 +1871,6 @@
     }
 
     @Test
-    public void testCreateBubbleFromOngoingNotification_OngoingDismissalDisabled() {
-        NotificationEntry notif = new NotificationEntryBuilder()
-                .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
-                .setCanBubble(true)
-                .build();
-
-        BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
-
-        assertFalse(
-                "Ongoing Notifis should be dismissable, if the feature is off",
-                bubble.isDismissable()
-        );
-    }
-
-
-    @Test
-    public void testCreateBubbleFromNoDismissNotification_OngoingDismissalDisabled() {
-        NotificationEntry notif = new NotificationEntryBuilder()
-                .setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
-                .setCanBubble(true)
-                .build();
-
-        BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
-
-        assertTrue(
-                "FLAG_NO_DISMISS should be ignored, if the feature is off",
-                bubble.isDismissable()
-        );
-    }
-
-    @Test
     public void registerBubbleBarListener_barDisabled_largeScreen_shouldBeIgnored() {
         mBubbleProperties.mIsBubbleBarEnabled = false;
         mPositioner.setIsLargeScreen(true);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
index b94f816e..36fa7e6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
@@ -62,6 +62,32 @@
         }
     }
 
+    /**
+     * Set the given flag's default value if no other value has been set.
+     *
+     * REMINDER: You should always test your code with your flag in both configurations, so
+     *  generally you should be setting a particular value.  This method should be reserved for
+     *  situations where the flag needs to be read (e.g. in the class constructor), but its
+     *  value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+     *  this method than to hard-code `false` or `true` because then at least if you're wrong,
+     *  and the flag value *does* matter, you'll notice when the flag is flipped and tests
+     *  start failing.
+     */
+    fun setDefault(flag: BooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
+    /**
+     * Set the given flag's default value if no other value has been set.
+     *
+     * REMINDER: You should always test your code with your flag in both configurations, so
+     *  generally you should be setting a particular value.  This method should be reserved for
+     *  situations where the flag needs to be read (e.g. in the class constructor), but its
+     *  value shouldn't affect the actual test cases. In those cases, it's mildly safer to use
+     *  this method than to hard-code `false` or `true` because then at least if you're wrong,
+     *  and the flag value *does* matter, you'll notice when the flag is flipped and tests
+     *  start failing.
+     */
+    fun setDefault(flag: SysPropBooleanFlag) = booleanFlags.putIfAbsent(flag.id, flag.default)
+
     private fun notifyFlagChanged(flag: Flag<*>) {
         flagListeners[flag.id]?.let { listeners ->
             listeners.forEach { listener ->
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
index 2715aaa..548169e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFaceAuthRepository.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.keyguard.data.repository
 
 import com.android.keyguard.FaceAuthUiEvent
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.FaceAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FaceDetectionStatus
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
@@ -29,16 +29,16 @@
 
     override val isAuthenticated = MutableStateFlow(false)
     override val canRunFaceAuth = MutableStateFlow(false)
-    private val _authenticationStatus = MutableStateFlow<AuthenticationStatus?>(null)
-    override val authenticationStatus: Flow<AuthenticationStatus> =
+    private val _authenticationStatus = MutableStateFlow<FaceAuthenticationStatus?>(null)
+    override val authenticationStatus: Flow<FaceAuthenticationStatus> =
         _authenticationStatus.filterNotNull()
-    fun setAuthenticationStatus(status: AuthenticationStatus) {
+    fun setAuthenticationStatus(status: FaceAuthenticationStatus) {
         _authenticationStatus.value = status
     }
-    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
-    override val detectionStatus: Flow<DetectionStatus>
+    private val _detectionStatus = MutableStateFlow<FaceDetectionStatus?>(null)
+    override val detectionStatus: Flow<FaceDetectionStatus>
         get() = _detectionStatus.filterNotNull()
-    fun setDetectionStatus(status: DetectionStatus) {
+    fun setDetectionStatus(status: FaceDetectionStatus) {
         _detectionStatus.value = status
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 4bfd3d6..38791ca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -17,32 +17,38 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.filterNotNull
 
 class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
     private val _isLockedOut = MutableStateFlow(false)
     override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
-
-    private val _isRunning = MutableStateFlow(false)
-    override val isRunning: Flow<Boolean>
-        get() = _isRunning
-
-    private var fpSensorType = MutableStateFlow<BiometricType?>(null)
-    override val availableFpSensorType: Flow<BiometricType?>
-        get() = fpSensorType
-
     fun setLockedOut(lockedOut: Boolean) {
         _isLockedOut.value = lockedOut
     }
 
+    private val _isRunning = MutableStateFlow(false)
+    override val isRunning: Flow<Boolean>
+        get() = _isRunning
     fun setIsRunning(value: Boolean) {
         _isRunning.value = value
     }
 
+    private var fpSensorType = MutableStateFlow<BiometricType?>(null)
+    override val availableFpSensorType: Flow<BiometricType?>
+        get() = fpSensorType
     fun setAvailableFpSensorType(value: BiometricType?) {
         fpSensorType.value = value
     }
+
+    private var _authenticationStatus = MutableStateFlow<FingerprintAuthenticationStatus?>(null)
+    override val authenticationStatus: Flow<FingerprintAuthenticationStatus>
+        get() = _authenticationStatus.filterNotNull()
+    fun setAuthenticationStatus(status: FingerprintAuthenticationStatus) {
+        _authenticationStatus.value = status
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index b9d098f..8428566 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -22,11 +22,14 @@
 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.ScreenModel
+import com.android.systemui.keyguard.shared.model.ScreenState
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.WakeSleepReason
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.keyguard.shared.model.WakefulnessState
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -56,6 +59,9 @@
     private val _isDozing = MutableStateFlow(false)
     override val isDozing: StateFlow<Boolean> = _isDozing
 
+    private val _dozeTimeTick = MutableSharedFlow<Unit>()
+    override val dozeTimeTick = _dozeTimeTick
+
     private val _lastDozeTapToWakePosition = MutableStateFlow<Point?>(null)
     override val lastDozeTapToWakePosition = _lastDozeTapToWakePosition.asStateFlow()
 
@@ -86,6 +92,9 @@
         )
     override val wakefulness = _wakefulnessModel
 
+    private val _screenModel = MutableStateFlow(ScreenModel(ScreenState.SCREEN_OFF))
+    override val screenModel = _screenModel
+
     private val _isUdfpsSupported = MutableStateFlow(false)
 
     private val _isKeyguardGoingAway = MutableStateFlow(false)
@@ -147,6 +156,10 @@
         _isDozing.value = isDozing
     }
 
+    override fun dozeTimeTick() {
+        _dozeTimeTick.tryEmit(Unit)
+    }
+
     override fun setLastDozeTapToWakePosition(position: Point) {
         _lastDozeTapToWakePosition.value = position
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 56837e8..03e3423 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -20,7 +20,6 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
 
 import java.util.List;
 
@@ -65,10 +64,6 @@
     }
 
     @Override
-    public void setMobileIcons(String slot, List<MobileIconState> states) {
-    }
-
-    @Override
     public void setNewMobileIconSubIds(List<Integer> subIds) {
     }
 
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 2e0a946..1f0181f 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -33,7 +33,7 @@
         "dagger2",
         "jsr330",
     ],
-    kotlincflags: ["-Xjvm-default=enable"],
+    kotlincflags: ["-Xjvm-default=all"],
     java_version: "1.8",
     sdk_version: "current",
     min_sdk_version: "current",
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index fee485d..896444d 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -35,14 +35,12 @@
 
     interface TransitionProgressListener {
         /** Called when transition is started */
-        @JvmDefault
         fun onTransitionStarted() {}
 
         /**
          * Called whenever transition progress is updated, [progress] is a value of the animation
          * where 0 is fully folded, 1 is fully unfolded
          */
-        @JvmDefault
         fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
 
         /**
@@ -51,11 +49,9 @@
          * For example, in [PhysicsBasedUnfoldTransitionProgressProvider] this could happen when the
          * animation is not tied to the hinge angle anymore and it is about to run fixed animation.
          */
-        @JvmDefault
         fun onTransitionFinishing() {}
 
         /** Called when transition is completely finished */
-        @JvmDefault
         fun onTransitionFinished() {}
     }
 }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 0af372f..bce7e88 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -31,9 +31,9 @@
     val isFinishedOpening: Boolean
 
     interface FoldUpdatesListener {
-        @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
-        @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {}
-        @JvmDefault fun onUnfoldedScreenAvailable() {}
+        fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
+        fun onFoldUpdate(@FoldUpdate update: Int) {}
+        fun onUnfoldedScreenAvailable() {}
     }
 
     @IntDef(
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1482d07..3f3fa34 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -246,6 +246,31 @@
         }
 
         @GuardedBy("mLock")
+        boolean isAtEdge() {
+            return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+        }
+
+        @GuardedBy("mLock")
+        boolean isAtLeftEdge() {
+            return getOffsetX() == getMaxOffsetXLocked();
+        }
+
+        @GuardedBy("mLock")
+        boolean isAtRightEdge() {
+            return getOffsetX() == getMinOffsetXLocked();
+        }
+
+        @GuardedBy("mLock")
+        boolean isAtTopEdge() {
+            return getOffsetY() == getMaxOffsetYLocked();
+        }
+
+        @GuardedBy("mLock")
+        boolean isAtBottomEdge() {
+            return getOffsetY() == getMinOffsetYLocked();
+        }
+
+        @GuardedBy("mLock")
         float getCenterX() {
             return (mMagnificationBounds.width() / 2.0f
                     + mMagnificationBounds.left - getOffsetX()) / getScale();
@@ -1086,6 +1111,87 @@
     }
 
     /**
+     * Returns whether the user is at one of the edges (left, right, top, bottom)
+     * of the magnification viewport
+     *
+     * @param displayId
+     * @return if user is at the edge of the view
+     */
+    public boolean isAtEdge(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isAtEdge();
+        }
+    }
+
+    /**
+     * Returns whether the user is at the left edge of the viewport
+     *
+     * @param displayId
+     * @return if user is at left edge of view
+     */
+    public boolean isAtLeftEdge(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isAtLeftEdge();
+        }
+    }
+
+    /**
+     * Returns whether the user is at the right edge of the viewport
+     *
+     * @param displayId
+     * @return if user is at right edge of view
+     */
+    public boolean isAtRightEdge(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isAtRightEdge();
+        }
+    }
+
+    /**
+     * Returns whether the user is at the top edge of the viewport
+     *
+     * @param displayId
+     * @return if user is at top edge of view
+     */
+    public boolean isAtTopEdge(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isAtTopEdge();
+        }
+    }
+
+    /**
+     * Returns whether the user is at the bottom edge of the viewport
+     *
+     * @param displayId
+     * @return if user is at bottom edge of view
+     */
+    public boolean isAtBottomEdge(int displayId) {
+        synchronized (mLock) {
+            final DisplayMagnification display = mDisplays.get(displayId);
+            if (display == null) {
+                return false;
+            }
+            return display.isAtBottomEdge();
+        }
+    }
+
+    /**
      * Returns the Y offset of the magnification viewport. If an animation
      * is in progress, this reflects the end state of the animation.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 038847e..4aebbf1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@
     @VisibleForTesting final DetectingState mDetectingState;
     @VisibleForTesting final PanningScalingState mPanningScalingState;
     @VisibleForTesting final ViewportDraggingState mViewportDraggingState;
+    @VisibleForTesting final SinglePanningState mSinglePanningState;
 
     private final ScreenStateReceiver mScreenStateReceiver;
     private final WindowMagnificationPromptController mPromptController;
@@ -146,7 +147,7 @@
 
     private PointerCoords[] mTempPointerCoords;
     private PointerProperties[] mTempPointerProperties;
-
+    @VisibleForTesting boolean mIsSinglePanningEnabled;
     public FullScreenMagnificationGestureHandler(@UiContext Context context,
             FullScreenMagnificationController fullScreenMagnificationController,
             AccessibilityTraceManager trace,
@@ -202,6 +203,8 @@
         mDetectingState = new DetectingState(context);
         mViewportDraggingState = new ViewportDraggingState();
         mPanningScalingState = new PanningScalingState(context);
+        mSinglePanningState = new SinglePanningState(context);
+        setSinglePanningEnabled(false);
 
         if (mDetectShortcutTrigger) {
             mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -213,6 +216,11 @@
         transitionTo(mDetectingState);
     }
 
+    @VisibleForTesting
+    void setSinglePanningEnabled(boolean isEnabled) {
+        mIsSinglePanningEnabled = isEnabled;
+    }
+
     @Override
     void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         handleEventWith(mCurrentState, event, rawEvent, policyFlags);
@@ -223,6 +231,7 @@
         // To keep InputEventConsistencyVerifiers within GestureDetectors happy
         mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
         mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
+        mSinglePanningState.mScrollGestureDetector.onTouchEvent(event);
 
         try {
             stateHandler.onMotionEvent(event, rawEvent, policyFlags);
@@ -669,7 +678,6 @@
 
         @Override
         public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-
             // Ensures that the state at the end of delegation is consistent with the last delegated
             // UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise
             switch (event.getActionMasked()) {
@@ -726,6 +734,8 @@
 
         @VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
 
+        private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+
         DetectingState(Context context) {
             mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
             mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -765,10 +775,11 @@
             cacheDelayedMotionEvent(event, rawEvent, policyFlags);
             switch (event.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN: {
-
                     mLastDetectingDownEventTime = event.getDownTime();
                     mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
 
+                    mFirstPointerDownLocation.set(event.getX(), event.getY());
+
                     if (!mFullScreenMagnificationController.magnificationRegionContains(
                             mDisplayId, event.getX(), event.getY())) {
 
@@ -800,7 +811,7 @@
                 break;
                 case ACTION_POINTER_DOWN: {
                     if (isActivated() && event.getPointerCount() == 2) {
-                        storeSecondPointerDownLocation(event);
+                        storePointerDownLocation(mSecondPointerDownLocation, event);
                         mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
                                 ViewConfiguration.getTapTimeout());
                     } else {
@@ -815,7 +826,6 @@
                 case ACTION_MOVE: {
                     if (isFingerDown()
                             && distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
-
                         // Swipe detected - transition immediately
 
                         // For convenience, viewport dragging takes precedence
@@ -826,10 +836,15 @@
                         } else if (isActivated() && event.getPointerCount() == 2) {
                             //Primary pointer is swiping, so transit to PanningScalingState
                             transitToPanningScalingStateAndClear();
+                        } else if (mIsSinglePanningEnabled
+                                && isActivated()
+                                && event.getPointerCount() == 1
+                                && !isOverscroll(event)) {
+                            transitToSinglePanningStateAndClear();
                         } else {
                             transitionToDelegatingStateAndClear();
                         }
-                    } else if (isActivated() && secondPointerDownValid()
+                    } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
                             && distanceClosestPointerToPoint(
                             mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
                         //Second pointer is swiping, so transit to PanningScalingState
@@ -843,11 +858,9 @@
 
                     if (!mFullScreenMagnificationController.magnificationRegionContains(
                             mDisplayId, event.getX(), event.getY())) {
-
                         transitionToDelegatingStateAndClear();
 
                     } else if (isMultiTapTriggered(3 /* taps */)) {
-
                         onTripleTap(/* up */ event);
 
                     } else if (
@@ -856,7 +869,6 @@
                             //TODO long tap should never happen here
                             && ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
                                     || (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
-
                         transitionToDelegatingStateAndClear();
 
                     }
@@ -865,14 +877,28 @@
             }
         }
 
-        private void storeSecondPointerDownLocation(MotionEvent event) {
-            final int index = event.getActionIndex();
-            mSecondPointerDownLocation.set(event.getX(index), event.getY(index));
+        private boolean isOverscroll(MotionEvent event) {
+            if (!pointerDownValid(mFirstPointerDownLocation)) {
+                return false;
+            }
+            float dX = event.getX() - mFirstPointerDownLocation.x;
+            float dY = event.getY() - mFirstPointerDownLocation.y;
+            boolean didOverscroll =
+                    mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0
+                    || mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0
+                    || mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
+                    || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0;
+            return didOverscroll;
         }
 
-        private boolean secondPointerDownValid() {
-            return !(Float.isNaN(mSecondPointerDownLocation.x) && Float.isNaN(
-                    mSecondPointerDownLocation.y));
+        private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
+            final int index = event.getActionIndex();
+            pointerDownLocation.set(event.getX(index), event.getY(index));
+        }
+
+        private boolean pointerDownValid(PointF pointerDownLocation) {
+            return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
+                    pointerDownLocation.y));
         }
 
         private void transitToPanningScalingStateAndClear() {
@@ -880,6 +906,11 @@
             clear();
         }
 
+        private void transitToSinglePanningStateAndClear() {
+            transitionTo(mSinglePanningState);
+            clear();
+        }
+
         public boolean isMultiTapTriggered(int numTaps) {
 
             // Shortcut acts as the 2 initial taps
@@ -947,6 +978,7 @@
             setShortcutTriggered(false);
             removePendingDelayedMessages();
             clearDelayedMotionEvents();
+            mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
             mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
         }
 
@@ -1165,12 +1197,14 @@
                 + ", mDelegatingState=" + mDelegatingState
                 + ", mMagnifiedInteractionState=" + mPanningScalingState
                 + ", mViewportDraggingState=" + mViewportDraggingState
+                + ", mSinglePanningState=" + mSinglePanningState
                 + ", mDetectTripleTap=" + mDetectTripleTap
                 + ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
                 + ", mCurrentState=" + State.nameOf(mCurrentState)
                 + ", mPreviousState=" + State.nameOf(mPreviousState)
                 + ", mMagnificationController=" + mFullScreenMagnificationController
                 + ", mDisplayId=" + mDisplayId
+                + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
                 + '}';
     }
 
@@ -1285,8 +1319,67 @@
      * Indicates an error with a gesture handler or state.
      */
     private static class GestureException extends Exception {
+
         GestureException(String message) {
             super(message);
         }
     }
+
+    final class SinglePanningState extends SimpleOnGestureListener implements State {
+        private final GestureDetector mScrollGestureDetector;
+        private MotionEventInfo mEvent;
+
+        SinglePanningState(Context context) {
+            mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+        }
+
+        @Override
+        public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+            int action = event.getActionMasked();
+            switch (action) {
+                case ACTION_UP:
+                case ACTION_CANCEL:
+                    clear();
+                    transitionTo(mDetectingState);
+                    break;
+            }
+        }
+
+        @Override
+        public boolean onScroll(
+                MotionEvent first, MotionEvent second, float distanceX, float distanceY) {
+            if (mCurrentState != mSinglePanningState) {
+                return true;
+            }
+            mFullScreenMagnificationController.offsetMagnifiedRegion(
+                    mDisplayId,
+                    distanceX,
+                    distanceY,
+                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+            if (DEBUG_PANNING_SCALING) {
+                Slog.i(
+                        mLogTag,
+                        "SinglePanningState Panned content by scrollX: "
+                                + distanceX
+                                + " scrollY: "
+                                + distanceY
+                                + " isAtEdge: "
+                                + mFullScreenMagnificationController.isAtEdge(mDisplayId));
+            }
+            // TODO: b/280812104 Dispatch events before Delegation
+            if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
+                clear();
+                transitionTo(mDelegatingState);
+            }
+            return /* event consumed: */ true;
+        }
+
+        @Override
+        public String toString() {
+            return "SinglePanningState{"
+                    + "isEdgeOfView="
+                    + mFullScreenMagnificationController.isAtEdge(mDisplayId)
+                    + "}";
+        }
+    }
 }
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index ca1ab9b..315972c 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -42,6 +42,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManagerInternal;
 import android.app.ActivityThread;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.app.assist.ActivityId;
 import android.content.ComponentName;
 import android.content.ContentCaptureOptions;
@@ -94,10 +95,12 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.infra.AbstractRemoteService;
 import com.android.internal.infra.GlobalWhitelistState;
+import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
 import com.android.server.contentprotection.ContentProtectionBlocklistManager;
+import com.android.server.contentprotection.ContentProtectionConsentManager;
 import com.android.server.contentprotection.ContentProtectionPackageManager;
 import com.android.server.contentprotection.RemoteContentProtectionService;
 import com.android.server.infra.AbstractMasterSystemService;
@@ -216,6 +219,8 @@
 
     @Nullable private final ContentProtectionBlocklistManager mContentProtectionBlocklistManager;
 
+    @Nullable private final ContentProtectionConsentManager mContentProtectionConsentManager;
+
     public ContentCaptureManagerService(@NonNull Context context) {
         super(context, new FrameworkResourcesServiceNameResolver(context,
                 com.android.internal.R.string.config_defaultContentCaptureService),
@@ -260,12 +265,15 @@
                 mContentProtectionBlocklistManager = createContentProtectionBlocklistManager();
                 mContentProtectionBlocklistManager.updateBlocklist(
                         mDevCfgContentProtectionAppsBlocklistSize);
+                mContentProtectionConsentManager = createContentProtectionConsentManager();
             } else {
                 mContentProtectionBlocklistManager = null;
+                mContentProtectionConsentManager = null;
             }
         } else {
             mContentProtectionServiceComponentName = null;
             mContentProtectionBlocklistManager = null;
+            mContentProtectionConsentManager = null;
         }
     }
 
@@ -802,6 +810,17 @@
                 new ContentProtectionPackageManager(getContext()));
     }
 
+    /** @hide */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @NonNull
+    protected ContentProtectionConsentManager createContentProtectionConsentManager() {
+        // Same handler as used by AbstractMasterSystemService
+        return new ContentProtectionConsentManager(
+                BackgroundThread.getHandler(),
+                getContext().getContentResolver(),
+                LocalServices.getService(DevicePolicyManagerInternal.class));
+    }
+
     @Nullable
     private ComponentName getContentProtectionServiceComponentName() {
         String flatComponentName = getContentProtectionServiceFlatComponentName();
@@ -1213,7 +1232,7 @@
                 isContentCaptureReceiverEnabled =
                         isContentCaptureReceiverEnabled(userId, packageName);
                 isContentProtectionReceiverEnabled =
-                        isContentProtectionReceiverEnabled(packageName);
+                        isContentProtectionReceiverEnabled(userId, packageName);
 
                 if (!isContentCaptureReceiverEnabled) {
                     // Full package is not allowlisted: check individual components next
@@ -1284,13 +1303,13 @@
         @Override // from GlobalWhitelistState
         public boolean isWhitelisted(@UserIdInt int userId, @NonNull String packageName) {
             return isContentCaptureReceiverEnabled(userId, packageName)
-                    || isContentProtectionReceiverEnabled(packageName);
+                    || isContentProtectionReceiverEnabled(userId, packageName);
         }
 
         @Override // from GlobalWhitelistState
         public boolean isWhitelisted(@UserIdInt int userId, @NonNull ComponentName componentName) {
             return super.isWhitelisted(userId, componentName)
-                    || isContentProtectionReceiverEnabled(componentName.getPackageName());
+                    || isContentProtectionReceiverEnabled(userId, componentName.getPackageName());
         }
 
         private boolean isContentCaptureReceiverEnabled(
@@ -1298,9 +1317,11 @@
             return super.isWhitelisted(userId, packageName);
         }
 
-        private boolean isContentProtectionReceiverEnabled(@NonNull String packageName) {
+        private boolean isContentProtectionReceiverEnabled(
+                @UserIdInt int userId, @NonNull String packageName) {
             if (mContentProtectionServiceComponentName == null
-                    || mContentProtectionBlocklistManager == null) {
+                    || mContentProtectionBlocklistManager == null
+                    || mContentProtectionConsentManager == null) {
                 return false;
             }
             synchronized (mLock) {
@@ -1308,7 +1329,8 @@
                     return false;
                 }
             }
-            return mContentProtectionBlocklistManager.isAllowed(packageName);
+            return mContentProtectionConsentManager.isConsentGranted(userId)
+                    && mContentProtectionBlocklistManager.isAllowed(packageName);
         }
     }
 
diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
new file mode 100644
index 0000000..2eb758c
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionConsentManager.java
@@ -0,0 +1,109 @@
+/*
+ * 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.contentprotection;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Manages consent for content protection.
+ *
+ * @hide
+ */
+public class ContentProtectionConsentManager {
+
+    private static final String TAG = "ContentProtectionConsentManager";
+
+    private static final String KEY_PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent";
+
+    @NonNull private final ContentResolver mContentResolver;
+
+    @NonNull private final DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
+    @NonNull
+    public final ContentObserver mContentObserver;
+
+    private volatile boolean mCachedPackageVerifierConsent;
+
+    public ContentProtectionConsentManager(
+            @NonNull Handler handler,
+            @NonNull ContentResolver contentResolver,
+            @NonNull DevicePolicyManagerInternal devicePolicyManagerInternal) {
+        mContentResolver = contentResolver;
+        mDevicePolicyManagerInternal = devicePolicyManagerInternal;
+        mContentObserver = new SettingsObserver(handler);
+
+        contentResolver.registerContentObserver(
+                Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT),
+                /* notifyForDescendants= */ false,
+                mContentObserver,
+                UserHandle.USER_ALL);
+        mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+    }
+
+    /**
+     * Returns true if all the consents are granted
+     */
+    public boolean isConsentGranted(@UserIdInt int userId) {
+        return mCachedPackageVerifierConsent && !isUserOrganizationManaged(userId);
+    }
+
+    private boolean isPackageVerifierConsentGranted() {
+        // Not always cached internally
+        return Settings.Global.getInt(
+                        mContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, /* def= */ 0)
+                >= 1;
+    }
+
+    private boolean isUserOrganizationManaged(@UserIdInt int userId) {
+        // Cached internally
+        return mDevicePolicyManagerInternal.isUserOrganizationManaged(userId);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
+            final String property = uri.getLastPathSegment();
+            if (property == null) {
+                return;
+            }
+            switch (property) {
+                case KEY_PACKAGE_VERIFIER_USER_CONSENT:
+                    mCachedPackageVerifierConsent = isPackageVerifierConsentGranted();
+                    return;
+                default:
+                    Slog.w(TAG, "Ignoring unexpected property: " + property);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 7c3a75f..5a15f17 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -49,6 +49,7 @@
     private static final String TAG = "MasterClear";
     private boolean mWipeExternalStorage;
     private boolean mWipeEsims;
+    private boolean mShowWipeProgress = true;
 
     @Override
     public void onReceive(final Context context, final Intent intent) {
@@ -77,8 +78,12 @@
             return;
         }
 
-        final boolean shutdown = intent.getBooleanExtra("shutdown", false);
         final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
+
+        // Factory reset dialog has its own UI for the reset process, so suppress ours if indicated.
+        mShowWipeProgress = intent.getBooleanExtra(Intent.EXTRA_SHOW_WIPE_PROGRESS, true);
+
+        final boolean shutdown = intent.getBooleanExtra("shutdown", false);
         mWipeExternalStorage = intent.getBooleanExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, false);
         mWipeEsims = intent.getBooleanExtra(Intent.EXTRA_WIPE_ESIMS, false);
         final boolean forceWipe = intent.getBooleanExtra(Intent.EXTRA_FORCE_MASTER_CLEAR, false)
@@ -190,15 +195,19 @@
         public WipeDataTask(Context context, Thread chainedTask) {
             mContext = context;
             mChainedTask = chainedTask;
-            mProgressDialog = new ProgressDialog(context, R.style.Theme_DeviceDefault_System);
+            mProgressDialog = mShowWipeProgress
+                    ? new ProgressDialog(context, R.style.Theme_DeviceDefault_System)
+                    : null;
         }
 
         @Override
         protected void onPreExecute() {
-            mProgressDialog.setIndeterminate(true);
-            mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-            mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
-            mProgressDialog.show();
+            if (mProgressDialog != null) {
+                mProgressDialog.setIndeterminate(true);
+                mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+                mProgressDialog.setMessage(mContext.getText(R.string.progress_erasing));
+                mProgressDialog.show();
+            }
         }
 
         @Override
@@ -214,7 +223,9 @@
 
         @Override
         protected void onPostExecute(Void result) {
-            mProgressDialog.dismiss();
+            if (mProgressDialog != null && mProgressDialog.isShowing()) {
+                mProgressDialog.dismiss();
+            }
             mChainedTask.start();
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8012c26..26bcb98 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5083,6 +5083,10 @@
             // Tell anyone interested that we are done booting!
             SystemProperties.set("sys.boot_completed", "1");
             SystemProperties.set("dev.bootcomplete", "1");
+
+            // Start PSI monitoring in LMKD if it was skipped earlier.
+            ProcessList.startPsiMonitoringAfterBoot();
+
             mUserController.onBootComplete(
                     new IIntentReceiver.Stub() {
                         @Override
@@ -19915,7 +19919,8 @@
                     return superImpl.apply(code, new AttributionSource(shellUid,
                             Process.INVALID_PID, "com.android.shell",
                             attributionSource.getAttributionTag(), attributionSource.getToken(),
-                            /*renouncedPermissions*/ null, attributionSource.getNext()),
+                            /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+                            attributionSource.getNext()),
                             shouldCollectAsyncNotedOp, message, shouldCollectMessage,
                             skiProxyOperation);
                 } finally {
@@ -19968,7 +19973,8 @@
                     return superImpl.apply(clientId, code, new AttributionSource(shellUid,
                             Process.INVALID_PID, "com.android.shell",
                             attributionSource.getAttributionTag(), attributionSource.getToken(),
-                            /*renouncedPermissions*/ null, attributionSource.getNext()),
+                            /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+                            attributionSource.getNext()),
                             startIfModeDefault, shouldCollectAsyncNotedOp, message,
                             shouldCollectMessage, skipProxyOperation, proxyAttributionFlags,
                             proxiedAttributionFlags, attributionChainId);
@@ -19994,7 +20000,8 @@
                     superImpl.apply(clientId, code, new AttributionSource(shellUid,
                             Process.INVALID_PID, "com.android.shell",
                             attributionSource.getAttributionTag(), attributionSource.getToken(),
-                            /*renouncedPermissions*/ null, attributionSource.getNext()),
+                            /*renouncedPermissions*/ null, attributionSource.getDeviceId(),
+                            attributionSource.getNext()),
                             skipProxyOperation);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index acd9771..e484a6c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -357,6 +357,7 @@
     static final byte LMK_UPDATE_PROPS = 7;
     static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
     static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
+    static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
 
     // Low Memory Killer Daemon command codes.
     // These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1568,6 +1569,15 @@
         return true;
     }
 
+    /**
+     * {@hide}
+     */
+    public static void startPsiMonitoringAfterBoot() {
+        ByteBuffer buf = ByteBuffer.allocate(4);
+        buf.putInt(LMK_START_MONITORING);
+        writeLmkd(buf, null);
+    }
+
     private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
         if (!sLmkdConnection.isConnected()) {
             // try to connect immediately and then keep retrying
diff --git a/services/core/java/com/android/server/appop/OWNERS b/services/core/java/com/android/server/appop/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/core/java/com/android/server/appop/OWNERS
+++ b/services/core/java/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
 include /core/java/android/permission/OWNERS
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7404b19..9f958be 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -72,6 +72,8 @@
 import android.database.ContentObserver;
 import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManagerInternal;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
 import android.hardware.hdmi.HdmiAudioSystemClient;
 import android.hardware.hdmi.HdmiClient;
 import android.hardware.hdmi.HdmiControlManager;
@@ -180,6 +182,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.view.Display;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
@@ -977,6 +980,36 @@
 
     private AtomicBoolean mMasterMute = new AtomicBoolean(false);
 
+    private DisplayManager mDisplayManager;
+
+    private DisplayListener mDisplayListener =
+      new DisplayListener() {
+        @Override
+        public void onDisplayAdded(int displayId) {}
+
+        @Override
+        public void onDisplayRemoved(int displayId) {}
+
+        @Override
+        public void onDisplayChanged(int displayId) {
+            if (displayId != Display.DEFAULT_DISPLAY) {
+                return;
+            }
+            int displayState = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getState();
+            if (displayState == Display.STATE_ON) {
+                if (mMonitorRotation) {
+                    RotationHelper.enable();
+                }
+                AudioSystem.setParameters("screen_state=on");
+            } else {
+                if (mMonitorRotation) {
+                    //reduce wakeups (save current) by only listening when display is on
+                    RotationHelper.disable();
+                }
+                AudioSystem.setParameters("screen_state=off");
+            }
+        }
+      };
 
     ///////////////////////////////////////////////////////////////////////////
     // Construction
@@ -1255,6 +1288,8 @@
                 0 /* arg1 */,  0 /* arg2 */, null /* obj */,  0 /* delay */);
         queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
                 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+
+        mDisplayManager = context.getSystemService(DisplayManager.class);
     }
 
     private void initVolumeStreamStates() {
@@ -1351,8 +1386,6 @@
                 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
         intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_USER_BACKGROUND);
         intentFilter.addAction(Intent.ACTION_USER_FOREGROUND);
@@ -1382,6 +1415,8 @@
         } else {
             subscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionChangedListener);
         }
+
+        mDisplayManager.registerDisplayListener(mDisplayListener, mAudioHandler);
     }
 
     public void systemReady() {
@@ -9552,17 +9587,6 @@
             } else if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)
                     || action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 mDeviceBroker.receiveBtEvent(intent);
-            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                if (mMonitorRotation) {
-                    RotationHelper.enable();
-                }
-                AudioSystem.setParameters("screen_state=on");
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                if (mMonitorRotation) {
-                    //reduce wakeups (save current) by only listening when display is on
-                    RotationHelper.disable();
-                }
-                AudioSystem.setParameters("screen_state=off");
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 sendMsg(mAudioHandler,
                         MSG_CONFIGURATION_CHANGED,
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 78c3808..8a54ae5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -580,7 +580,7 @@
         }
         final BiometricSchedulerOperation operation = mCurrentOperation;
         mHandler.postDelayed(() -> {
-            if (operation == mCurrentOperation) {
+            if (operation == mCurrentOperation && !operation.isFinished()) {
                 Counter.logIncrement("biometric.value_scheduler_watchdog_triggered_count");
                 clearScheduler();
             }
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 5b11cfe..4bfc090 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -36,16 +36,18 @@
 
 
     BrightnessRangeController(HighBrightnessModeController hbmController,
-            Runnable modeChangeCallback) {
-        this(hbmController, modeChangeCallback,
+            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig) {
+        this(hbmController, modeChangeCallback, displayDeviceConfig,
                 new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
     }
 
     BrightnessRangeController(HighBrightnessModeController hbmController,
-            Runnable modeChangeCallback, DeviceConfigParameterProvider configParameterProvider) {
+            Runnable modeChangeCallback, DisplayDeviceConfig displayDeviceConfig,
+            DeviceConfigParameterProvider configParameterProvider) {
         mHbmController = hbmController;
         mModeChangeCallback = modeChangeCallback;
         mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+        mNormalBrightnessModeController.resetNbmData(displayDeviceConfig.getLuxThrottlingData());
     }
 
     void dump(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index dd5afa2..da51569 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -33,12 +33,15 @@
     private final String mDisplayBrightnessStrategyName;
     private final boolean mShouldUseAutoBrightness;
 
+    private final boolean mIsSlowChange;
+
     private DisplayBrightnessState(Builder builder) {
         mBrightness = builder.getBrightness();
         mSdrBrightness = builder.getSdrBrightness();
         mBrightnessReason = builder.getBrightnessReason();
         mDisplayBrightnessStrategyName = builder.getDisplayBrightnessStrategyName();
         mShouldUseAutoBrightness = builder.getShouldUseAutoBrightness();
+        mIsSlowChange = builder.isSlowChange();
     }
 
     /**
@@ -77,6 +80,13 @@
         return mShouldUseAutoBrightness;
     }
 
+    /**
+     * @return {@code true} if the should transit to new state slowly
+     */
+    public boolean isSlowChange() {
+        return mIsSlowChange;
+    }
+
     @Override
     public String toString() {
         StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -88,6 +98,8 @@
         stringBuilder.append(getBrightnessReason());
         stringBuilder.append("\n    shouldUseAutoBrightness:");
         stringBuilder.append(getShouldUseAutoBrightness());
+        stringBuilder.append("\n    isSlowChange:");
+        stringBuilder.append(mIsSlowChange);
         return stringBuilder.toString();
     }
 
@@ -111,13 +123,14 @@
                 && mBrightnessReason.equals(otherState.getBrightnessReason())
                 && TextUtils.equals(mDisplayBrightnessStrategyName,
                         otherState.getDisplayBrightnessStrategyName())
-                && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness();
+                && mShouldUseAutoBrightness == otherState.getShouldUseAutoBrightness()
+                && mIsSlowChange == otherState.isSlowChange();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(
-                mBrightness, mSdrBrightness, mBrightnessReason, mShouldUseAutoBrightness);
+        return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
+                mShouldUseAutoBrightness, mIsSlowChange);
     }
 
     /**
@@ -129,6 +142,7 @@
         private BrightnessReason mBrightnessReason = new BrightnessReason();
         private String mDisplayBrightnessStrategyName;
         private boolean mShouldUseAutoBrightness;
+        private boolean mIsSlowChange;
 
         /**
          * Create a builder starting with the values from the specified {@link
@@ -143,6 +157,7 @@
             builder.setBrightnessReason(state.getBrightnessReason());
             builder.setDisplayBrightnessStrategyName(state.getDisplayBrightnessStrategyName());
             builder.setShouldUseAutoBrightness(state.getShouldUseAutoBrightness());
+            builder.setIsSlowChange(state.isSlowChange());
             return builder;
         }
 
@@ -237,6 +252,21 @@
         }
 
         /**
+         * See {@link DisplayBrightnessState#isSlowChange()}.
+         */
+        public Builder setIsSlowChange(boolean shouldUseAutoBrightness) {
+            this.mIsSlowChange = shouldUseAutoBrightness;
+            return this;
+        }
+
+        /**
+         * See {@link DisplayBrightnessState#isSlowChange()}.
+         */
+        public boolean isSlowChange() {
+            return mIsSlowChange;
+        }
+
+        /**
          * This is used to construct an immutable DisplayBrightnessState object from its builder
          */
         public DisplayBrightnessState build() {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dee49e8..75c15eb 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -673,7 +673,7 @@
         HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
 
         mBrightnessRangeController = new BrightnessRangeController(hbmController,
-                modeChangeCallback);
+                modeChangeCallback, mDisplayDeviceConfig);
 
         mBrightnessThrottler = createBrightnessThrottlerLocked();
 
@@ -2288,8 +2288,17 @@
             if (!reportOnly && mPowerState.getScreenState() != state
                     && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
-                // TODO(b/153319140) remove when we can get this from the above trace invocation
-                SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
+
+                String propertyKey = "debug.tracing.screen_state";
+                String propertyValue = String.valueOf(state);
+                try {
+                    // TODO(b/153319140) remove when we can get this from the above trace invocation
+                    SystemProperties.set(propertyKey, propertyValue);
+                } catch (RuntimeException e) {
+                    Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+                            + " value=" + propertyValue + " " + e.getMessage());
+                }
+
                 mPowerState.setScreenState(state);
                 // Tell battery stats about the transition.
                 noteScreenState(state);
@@ -2382,8 +2391,17 @@
         }
         if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
             Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
-            // TODO(b/153319140) remove when we can get this from the above trace invocation
-            SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
+
+            String propertyKey = "debug.tracing.screen_brightness";
+            String propertyValue = String.valueOf(target);
+            try {
+                // TODO(b/153319140) remove when we can get this from the above trace invocation
+                SystemProperties.set(propertyKey, propertyValue);
+            } catch (RuntimeException e) {
+                Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+                        + " value=" + propertyValue + " " + e.getMessage());
+            }
+
             noteScreenBrightness(target);
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 7e53d9e..42683d8 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -444,9 +444,6 @@
     @Nullable
     private BrightnessMappingStrategy mIdleModeBrightnessMapper;
 
-    // Indicates whether we should ramp slowly to the brightness value to follow.
-    private boolean mBrightnessToFollowSlowChange;
-
     private boolean mIsRbcActive;
 
     // Animators.
@@ -555,7 +552,7 @@
         mBrightnessThrottler = createBrightnessThrottlerLocked();
 
         mBrightnessRangeController = new BrightnessRangeController(hbmController,
-                modeChangeCallback);
+                modeChangeCallback, mDisplayDeviceConfig);
 
         mDisplayBrightnessController =
                 new DisplayBrightnessController(context, null,
@@ -1291,7 +1288,7 @@
         // actual state instead of the desired one.
         animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition());
         state = mPowerState.getScreenState();
-        boolean slowChange = false;
+
         final boolean userSetBrightnessChanged = mDisplayBrightnessController
                 .updateUserSetScreenBrightness();
 
@@ -1300,11 +1297,7 @@
         float brightnessState = displayBrightnessState.getBrightness();
         float rawBrightnessState = displayBrightnessState.getBrightness();
         mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason());
-
-        if (displayBrightnessState.getBrightnessReason().getReason()
-                == BrightnessReason.REASON_FOLLOWER) {
-            slowChange = mBrightnessToFollowSlowChange;
-        }
+        boolean slowChange = displayBrightnessState.isSlowChange();
 
         // Set up the ScreenOff controller used when coming out of SCREEN_OFF and the ALS sensor
         // doesn't yet have a valid lux value to use with auto-brightness.
@@ -1354,6 +1347,7 @@
                             .getRawAutomaticScreenBrightness();
                     brightnessState = clampScreenBrightness(brightnessState);
                     // slowly adapt to auto-brightness
+                    // TODO(b/253226419): slowChange should be decided by strategy.updateBrightness
                     slowChange = mAutomaticBrightnessStrategy.hasAppliedAutoBrightness()
                             && !mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged();
                     brightnessAdjustmentFlags =
@@ -1955,8 +1949,17 @@
             if (!reportOnly && mPowerState.getScreenState() != state
                     && readyToUpdateDisplayState()) {
                 Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
-                // TODO(b/153319140) remove when we can get this from the above trace invocation
-                SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
+
+                String propertyKey = "debug.tracing.screen_state";
+                String propertyValue = String.valueOf(state);
+                try {
+                    // TODO(b/153319140) remove when we can get this from the above trace invocation
+                    SystemProperties.set(propertyKey, propertyValue);
+                } catch (RuntimeException e) {
+                    Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+                            + " value=" + propertyValue + " " + e.getMessage());
+                }
+
                 mPowerState.setScreenState(state);
                 // Tell battery stats about the transition.
                 noteScreenState(state);
@@ -2031,8 +2034,17 @@
         }
         if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
             Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
-            // TODO(b/153319140) remove when we can get this from the above trace invocation
-            SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
+
+            String propertyKey = "debug.tracing.screen_brightness";
+            String propertyValue = String.valueOf(target);
+            try {
+                // TODO(b/153319140) remove when we can get this from the above trace invocation
+                SystemProperties.set(propertyKey, propertyValue);
+            } catch (RuntimeException e) {
+                Slog.e(mTag, "Failed to set a system property: key=" + propertyKey
+                        + " value=" + propertyValue + " " + e.getMessage());
+            }
+
             noteScreenBrightness(target);
         }
     }
@@ -2259,17 +2271,17 @@
             boolean slowChange) {
         mBrightnessRangeController.onAmbientLuxChange(ambientLux);
         if (nits < 0) {
-            mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
+            mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange);
         } else {
             float brightness = mDisplayBrightnessController.convertToFloatScale(nits);
             if (BrightnessUtils.isValidBrightnessValue(brightness)) {
-                mDisplayBrightnessController.setBrightnessToFollow(brightness);
+                mDisplayBrightnessController.setBrightnessToFollow(brightness, slowChange);
             } else {
                 // The device does not support nits
-                mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
+                mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness,
+                        slowChange);
             }
         }
-        mBrightnessToFollowSlowChange = slowChange;
         sendUpdatePowerState();
     }
 
@@ -2409,7 +2421,6 @@
         pw.println("  mReportedToPolicy="
                 + reportedToPolicyToString(mReportedScreenStateToPolicy));
         pw.println("  mIsRbcActive=" + mIsRbcActive);
-        pw.println("  mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange);
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
         mAutomaticBrightnessStrategy.dump(ipw);
 
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
index 3fae224..8bf675c 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessUtils.java
@@ -54,6 +54,16 @@
     public static DisplayBrightnessState constructDisplayBrightnessState(
             int brightnessChangeReason, float brightness, float sdrBrightness,
             String displayBrightnessStrategyName) {
+        return constructDisplayBrightnessState(brightnessChangeReason, brightness, sdrBrightness,
+                displayBrightnessStrategyName, /* slowChange= */ false);
+    }
+
+    /**
+     * A utility to construct the DisplayBrightnessState
+     */
+    public static DisplayBrightnessState constructDisplayBrightnessState(
+            int brightnessChangeReason, float brightness, float sdrBrightness,
+            String displayBrightnessStrategyName, boolean slowChange) {
         BrightnessReason brightnessReason = new BrightnessReason();
         brightnessReason.setReason(brightnessChangeReason);
         return new DisplayBrightnessState.Builder()
@@ -61,6 +71,7 @@
                 .setSdrBrightness(sdrBrightness)
                 .setBrightnessReason(brightnessReason)
                 .setDisplayBrightnessStrategyName(displayBrightnessStrategyName)
+                .setIsSlowChange(slowChange)
                 .build();
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index ffd62a3..d6f0098 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -164,10 +164,10 @@
     /**
      * Sets the brightness to follow
      */
-    public void setBrightnessToFollow(Float brightnessToFollow) {
+    public void setBrightnessToFollow(float brightnessToFollow, boolean slowChange) {
         synchronized (mLock) {
             mDisplayBrightnessStrategySelector.getFollowerDisplayBrightnessStrategy()
-                    .setBrightnessToFollow(brightnessToFollow);
+                    .setBrightnessToFollow(brightnessToFollow, slowChange);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
index 090ec13..585f576 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FollowerBrightnessStrategy.java
@@ -37,9 +37,13 @@
     // Set to PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no brightness to follow set.
     private float mBrightnessToFollow;
 
+    // Indicates whether we should ramp slowly to the brightness value to follow.
+    private boolean mBrightnessToFollowSlowChange;
+
     public FollowerBrightnessStrategy(int displayId) {
         mDisplayId = displayId;
         mBrightnessToFollow = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+        mBrightnessToFollowSlowChange = false;
     }
 
     @Override
@@ -48,7 +52,7 @@
         // Todo(b/241308599): Introduce a validator class and add validations before setting
         // the brightness
         return BrightnessUtils.constructDisplayBrightnessState(BrightnessReason.REASON_FOLLOWER,
-                mBrightnessToFollow, mBrightnessToFollow, getName());
+                mBrightnessToFollow, mBrightnessToFollow, getName(), mBrightnessToFollowSlowChange);
     }
 
     @Override
@@ -60,8 +64,12 @@
         return mBrightnessToFollow;
     }
 
-    public void setBrightnessToFollow(float brightnessToFollow) {
+    /**
+     * Updates brightness value and brightness slowChange flag
+     **/
+    public void setBrightnessToFollow(float brightnessToFollow, boolean slowChange) {
         mBrightnessToFollow = brightnessToFollow;
+        mBrightnessToFollowSlowChange = slowChange;
     }
 
     /**
@@ -71,5 +79,6 @@
         writer.println("FollowerBrightnessStrategy:");
         writer.println("  mDisplayId=" + mDisplayId);
         writer.println("  mBrightnessToFollow:" + mBrightnessToFollow);
+        writer.println("  mBrightnessToFollowSlowChange:" + mBrightnessToFollowSlowChange);
     }
 }
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
index feebdf1..dfb5f62 100644
--- a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -60,6 +60,11 @@
                 DisplayManager.DeviceConfig.KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER, false);
     }
 
+    public boolean isDisableScreenWakeLocksWhileCachedFeatureEnabled() {
+        return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+                DisplayManager.DeviceConfig.KEY_DISABLE_SCREEN_WAKE_LOCKS_WHILE_CACHED, true);
+    }
+
     // feature: smooth_display_feature
     // parameter: peak_refresh_rate_default
     public float getPeakRefreshRateDefault() {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 633bf731..0e8a5fb 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -17,6 +17,8 @@
 package com.android.server.dreams;
 
 import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
+import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
+import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
 
 import android.app.ActivityTaskManager;
 import android.app.BroadcastOptions;
@@ -72,6 +74,7 @@
     private final Handler mHandler;
     private final Listener mListener;
     private final ActivityTaskManager mActivityTaskManager;
+    private final PowerManager mPowerManager;
 
     private final Intent mDreamingStartedIntent = new Intent(Intent.ACTION_DREAMING_STARTED)
             .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | FLAG_RECEIVER_FOREGROUND);
@@ -84,6 +87,15 @@
     private final Intent mCloseNotificationShadeIntent;
     private final Bundle mCloseNotificationShadeOptions;
 
+    /**
+     * If this flag is on, we report user activity to {@link PowerManager} so that the screen
+     * doesn't shut off immediately when a dream quits unexpectedly. The device will instead go to
+     * keyguard and time out back to dreaming shortly.
+     *
+     * This allows the dream a second chance to relaunch in case of an app update or other crash.
+     */
+    private final boolean mResetScreenTimeoutOnUnexpectedDreamExit;
+
     private DreamRecord mCurrentDream;
 
     // Whether a dreaming started intent has been broadcast.
@@ -101,6 +113,7 @@
         mHandler = handler;
         mListener = listener;
         mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
+        mPowerManager = mContext.getSystemService(PowerManager.class);
         mCloseNotificationShadeIntent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         mCloseNotificationShadeIntent.putExtra(EXTRA_REASON_KEY, EXTRA_REASON_VALUE);
         mCloseNotificationShadeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -110,6 +123,8 @@
                         EXTRA_REASON_VALUE)
                 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE)
                 .toBundle();
+        mResetScreenTimeoutOnUnexpectedDreamExit = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_resetScreenTimeoutOnUnexpectedDreamExit);
     }
 
     /**
@@ -235,6 +250,17 @@
     }
 
     /**
+     * Sends a user activity signal to PowerManager to stop the screen from turning off immediately
+     * if there hasn't been any user interaction in a while.
+     */
+    private void resetScreenTimeout() {
+        Slog.i(TAG, "Resetting screen timeout");
+        long time = SystemClock.uptimeMillis();
+        mPowerManager.userActivity(time, USER_ACTIVITY_EVENT_OTHER,
+                USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS);
+    }
+
+    /**
      * Stops dreaming.
      *
      * The current dream, if any, and any unstopped previous dreams are stopped. The device stops
@@ -448,6 +474,9 @@
             mHandler.post(() -> {
                 mService = null;
                 if (mCurrentDream == DreamRecord.this) {
+                    if (mResetScreenTimeoutOnUnexpectedDreamExit) {
+                        resetScreenTimeout();
+                    }
                     stopDream(true /*immediate*/, "binder died");
                 }
             });
@@ -473,6 +502,9 @@
             mHandler.post(() -> {
                 mService = null;
                 if (mCurrentDream == DreamRecord.this) {
+                    if (mResetScreenTimeoutOnUnexpectedDreamExit) {
+                        resetScreenTimeout();
+                    }
                     stopDream(true /*immediate*/, "service disconnected");
                 }
             });
diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
index eb2da34..4b30ae5 100644
--- a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
+++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java
@@ -21,12 +21,16 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Intent;
 import android.hardware.input.KeyboardLayout;
 import android.icu.util.ULocale;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.proto.ProtoOutputStream;
 import android.view.InputDevice;
+import android.view.KeyEvent;
 import android.view.inputmethod.InputMethodSubtype;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,8 +40,12 @@
 
 import java.lang.annotation.Retention;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Collect Keyboard metrics
@@ -50,13 +58,14 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     @Retention(SOURCE)
-    @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = {
+    @IntDef(prefix = {"LAYOUT_SELECTION_CRITERIA_"}, value = {
             LAYOUT_SELECTION_CRITERIA_USER,
             LAYOUT_SELECTION_CRITERIA_DEVICE,
             LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD,
             LAYOUT_SELECTION_CRITERIA_DEFAULT
     })
-    public @interface LayoutSelectionCriteria {}
+    public @interface LayoutSelectionCriteria {
+    }
 
     /** Manual selection by user */
     public static final int LAYOUT_SELECTION_CRITERIA_USER = 0;
@@ -76,17 +85,301 @@
     @VisibleForTesting
     static final String DEFAULT_LANGUAGE_TAG = "None";
 
+    public enum KeyboardLogEvent {
+        UNSPECIFIED(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__UNSPECIFIED,
+                "INVALID_KEYBOARD_EVENT"),
+        HOME(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME,
+                "HOME"),
+        RECENT_APPS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS,
+                "RECENT_APPS"),
+        BACK(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK,
+                "BACK"),
+        APP_SWITCH(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__APP_SWITCH,
+                "APP_SWITCH"),
+        LAUNCH_ASSISTANT(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_ASSISTANT,
+                "LAUNCH_ASSISTANT"),
+        LAUNCH_VOICE_ASSISTANT(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_VOICE_ASSISTANT,
+                "LAUNCH_VOICE_ASSISTANT"),
+        LAUNCH_SYSTEM_SETTINGS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SYSTEM_SETTINGS,
+                "LAUNCH_SYSTEM_SETTINGS"),
+        TOGGLE_NOTIFICATION_PANEL(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_NOTIFICATION_PANEL,
+                "TOGGLE_NOTIFICATION_PANEL"),
+        TOGGLE_TASKBAR(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_TASKBAR,
+                "TOGGLE_TASKBAR"),
+        TAKE_SCREENSHOT(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TAKE_SCREENSHOT,
+                "TAKE_SCREENSHOT"),
+        OPEN_SHORTCUT_HELPER(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_SHORTCUT_HELPER,
+                "OPEN_SHORTCUT_HELPER"),
+        BRIGHTNESS_UP(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_UP,
+                "BRIGHTNESS_UP"),
+        BRIGHTNESS_DOWN(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BRIGHTNESS_DOWN,
+                "BRIGHTNESS_DOWN"),
+        KEYBOARD_BACKLIGHT_UP(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_UP,
+                "KEYBOARD_BACKLIGHT_UP"),
+        KEYBOARD_BACKLIGHT_DOWN(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_DOWN,
+                "KEYBOARD_BACKLIGHT_DOWN"),
+        KEYBOARD_BACKLIGHT_TOGGLE(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__KEYBOARD_BACKLIGHT_TOGGLE,
+                "KEYBOARD_BACKLIGHT_TOGGLE"),
+        VOLUME_UP(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_UP,
+                "VOLUME_UP"),
+        VOLUME_DOWN(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_DOWN,
+                "VOLUME_DOWN"),
+        VOLUME_MUTE(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__VOLUME_MUTE,
+                "VOLUME_MUTE"),
+        ALL_APPS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ALL_APPS,
+                "ALL_APPS"),
+        LAUNCH_SEARCH(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_SEARCH,
+                "LAUNCH_SEARCH"),
+        LANGUAGE_SWITCH(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LANGUAGE_SWITCH,
+                "LANGUAGE_SWITCH"),
+        ACCESSIBILITY_ALL_APPS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__ACCESSIBILITY_ALL_APPS,
+                "ACCESSIBILITY_ALL_APPS"),
+        TOGGLE_CAPS_LOCK(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK,
+                "TOGGLE_CAPS_LOCK"),
+        SYSTEM_MUTE(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE,
+                "SYSTEM_MUTE"),
+        SPLIT_SCREEN_NAVIGATION(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION,
+                "SPLIT_SCREEN_NAVIGATION"),
+        TRIGGER_BUG_REPORT(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT,
+                "TRIGGER_BUG_REPORT"),
+        LOCK_SCREEN(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LOCK_SCREEN,
+                "LOCK_SCREEN"),
+        OPEN_NOTES(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__OPEN_NOTES,
+                "OPEN_NOTES"),
+        TOGGLE_POWER(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_POWER,
+                "TOGGLE_POWER"),
+        SYSTEM_NAVIGATION(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_NAVIGATION,
+                "SYSTEM_NAVIGATION"),
+        SLEEP(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SLEEP,
+                "SLEEP"),
+        WAKEUP(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__WAKEUP,
+                "WAKEUP"),
+        MEDIA_KEY(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__MEDIA_KEY,
+                "MEDIA_KEY"),
+        LAUNCH_DEFAULT_BROWSER(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_BROWSER,
+                "LAUNCH_DEFAULT_BROWSER"),
+        LAUNCH_DEFAULT_EMAIL(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_EMAIL,
+                "LAUNCH_DEFAULT_EMAIL"),
+        LAUNCH_DEFAULT_CONTACTS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CONTACTS,
+                "LAUNCH_DEFAULT_CONTACTS"),
+        LAUNCH_DEFAULT_CALENDAR(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALENDAR,
+                "LAUNCH_DEFAULT_CALENDAR"),
+        LAUNCH_DEFAULT_CALCULATOR(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_CALCULATOR,
+                "LAUNCH_DEFAULT_CALCULATOR"),
+        LAUNCH_DEFAULT_MUSIC(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MUSIC,
+                "LAUNCH_DEFAULT_MUSIC"),
+        LAUNCH_DEFAULT_MAPS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MAPS,
+                "LAUNCH_DEFAULT_MAPS"),
+        LAUNCH_DEFAULT_MESSAGING(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_MESSAGING,
+                "LAUNCH_DEFAULT_MESSAGING"),
+        LAUNCH_DEFAULT_GALLERY(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_GALLERY,
+                "LAUNCH_DEFAULT_GALLERY"),
+        LAUNCH_DEFAULT_FILES(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FILES,
+                "LAUNCH_DEFAULT_FILES"),
+        LAUNCH_DEFAULT_WEATHER(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_WEATHER,
+                "LAUNCH_DEFAULT_WEATHER"),
+        LAUNCH_DEFAULT_FITNESS(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_DEFAULT_FITNESS,
+                "LAUNCH_DEFAULT_FITNESS"),
+        LAUNCH_APPLICATION_BY_PACKAGE_NAME(
+                FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__LAUNCH_APPLICATION_BY_PACKAGE_NAME,
+                "LAUNCH_APPLICATION_BY_PACKAGE_NAME");
+
+        private final int mValue;
+        private final String mName;
+
+        private static final SparseArray<KeyboardLogEvent> VALUE_TO_ENUM_MAP = new SparseArray<>();
+
+        static {
+            for (KeyboardLogEvent type : KeyboardLogEvent.values()) {
+                VALUE_TO_ENUM_MAP.put(type.mValue, type);
+            }
+        }
+
+        KeyboardLogEvent(int enumValue, String enumName) {
+            mValue = enumValue;
+            mName = enumName;
+        }
+
+        public int getIntValue() {
+            return mValue;
+        }
+
+        /**
+         * Convert int value to corresponding KeyboardLogEvent enum. If can't find any matching
+         * value will return {@code null}
+         */
+        @Nullable
+        public static KeyboardLogEvent from(int value) {
+            return VALUE_TO_ENUM_MAP.get(value);
+        }
+
+        /**
+         * Find KeyboardLogEvent corresponding to volume up/down/mute key events.
+         */
+        @Nullable
+        public static KeyboardLogEvent getVolumeEvent(int keycode) {
+            switch (keycode) {
+                case KeyEvent.KEYCODE_VOLUME_DOWN:
+                    return VOLUME_DOWN;
+                case KeyEvent.KEYCODE_VOLUME_UP:
+                    return VOLUME_UP;
+                case KeyEvent.KEYCODE_VOLUME_MUTE:
+                    return VOLUME_MUTE;
+                default:
+                    return null;
+            }
+        }
+
+        /**
+         * Find KeyboardLogEvent corresponding to brightness up/down key events.
+         */
+        @Nullable
+        public static KeyboardLogEvent getBrightnessEvent(int keycode) {
+            switch (keycode) {
+                case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
+                    return BRIGHTNESS_DOWN;
+                case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+                    return BRIGHTNESS_UP;
+                default:
+                    return null;
+            }
+        }
+
+        /**
+         * Find KeyboardLogEvent corresponding to intent filter category. Returns
+         * {@code null if no matching event found}
+         */
+        @Nullable
+        public static KeyboardLogEvent getLogEventFromIntent(Intent intent) {
+            Intent selectorIntent = intent.getSelector();
+            if (selectorIntent != null) {
+                Set<String> selectorCategories = selectorIntent.getCategories();
+                if (selectorCategories != null && !selectorCategories.isEmpty()) {
+                    for (String intentCategory : selectorCategories) {
+                        KeyboardLogEvent logEvent = getEventFromSelectorCategory(intentCategory);
+                        if (logEvent == null) {
+                            continue;
+                        }
+                        return logEvent;
+                    }
+                }
+            }
+
+            Set<String> intentCategories = intent.getCategories();
+            if (intentCategories == null || intentCategories.isEmpty()
+                    || !intentCategories.contains(Intent.CATEGORY_LAUNCHER)) {
+                return null;
+            }
+            if (intent.getComponent() == null) {
+                return null;
+            }
+
+            // TODO(b/280423320): Add new field package name associated in the
+            //  KeyboardShortcutEvent atom and log it accordingly.
+            return LAUNCH_APPLICATION_BY_PACKAGE_NAME;
+        }
+
+        @Nullable
+        private static KeyboardLogEvent getEventFromSelectorCategory(String category) {
+            switch (category) {
+                case Intent.CATEGORY_APP_BROWSER:
+                    return LAUNCH_DEFAULT_BROWSER;
+                case Intent.CATEGORY_APP_EMAIL:
+                    return LAUNCH_DEFAULT_EMAIL;
+                case Intent.CATEGORY_APP_CONTACTS:
+                    return LAUNCH_DEFAULT_CONTACTS;
+                case Intent.CATEGORY_APP_CALENDAR:
+                    return LAUNCH_DEFAULT_CALENDAR;
+                case Intent.CATEGORY_APP_CALCULATOR:
+                    return LAUNCH_DEFAULT_CALCULATOR;
+                case Intent.CATEGORY_APP_MUSIC:
+                    return LAUNCH_DEFAULT_MUSIC;
+                case Intent.CATEGORY_APP_MAPS:
+                    return LAUNCH_DEFAULT_MAPS;
+                case Intent.CATEGORY_APP_MESSAGING:
+                    return LAUNCH_DEFAULT_MESSAGING;
+                case Intent.CATEGORY_APP_GALLERY:
+                    return LAUNCH_DEFAULT_GALLERY;
+                case Intent.CATEGORY_APP_FILES:
+                    return LAUNCH_DEFAULT_FILES;
+                case Intent.CATEGORY_APP_WEATHER:
+                    return LAUNCH_DEFAULT_WEATHER;
+                case Intent.CATEGORY_APP_FITNESS:
+                    return LAUNCH_DEFAULT_FITNESS;
+                default:
+                    return null;
+            }
+        }
+    }
+
     /**
      * Log keyboard system shortcuts for the proto
      * {@link com.android.os.input.KeyboardSystemsEventReported}
      * defined in "stats/atoms/input/input_extension_atoms.proto"
      */
-    public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice,
-            int keyboardSystemEvent, int[] keyCode, int modifierState) {
+    public static void logKeyboardSystemsEventReportedAtom(@Nullable InputDevice inputDevice,
+            @Nullable KeyboardLogEvent keyboardSystemEvent, int modifierState, int... keyCodes) {
+        // Logging Keyboard system event only for an external HW keyboard. We should not log events
+        // for virtual keyboards or internal Key events.
+        if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) {
+            return;
+        }
         int vendorId = inputDevice.getVendorId();
         int productId = inputDevice.getProductId();
+        if (keyboardSystemEvent == null) {
+            Slog.w(TAG, "Invalid keyboard event logging, keycode = " + Arrays.toString(keyCodes)
+                    + ", modifier state = " + modifierState);
+            return;
+        }
         FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
-                vendorId, productId, keyboardSystemEvent, keyCode, modifierState);
+                vendorId, productId, keyboardSystemEvent.getIntValue(), keyCodes, modifierState);
+
+        if (DEBUG) {
+            Slog.d(TAG, "Logging Keyboard system event: " + keyboardSystemEvent.mName);
+        }
     }
 
     /**
@@ -94,8 +387,8 @@
      * {@link com.android.os.input.KeyboardConfigured} atom
      *
      * @param event {@link KeyboardConfigurationEvent} contains information about keyboard
-     *               configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
-     *               configuration event to log.
+     *              configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the
+     *              configuration event to log.
      */
     public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) {
         // Creating proto to log nested field KeyboardLayoutConfig in atom
@@ -241,7 +534,7 @@
                     KeyboardLayout selectedLayout = mSelectedLayoutList.get(i);
                     @LayoutSelectionCriteria int layoutSelectionCriteria =
                             mLayoutSelectionCriteriaList.get(i);
-                    InputMethodSubtype imeSubtype =  mImeSubtypeList.get(i);
+                    InputMethodSubtype imeSubtype = mImeSubtypeList.get(i);
                     String keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag();
                     keyboardLanguageTag = keyboardLanguageTag == null ? DEFAULT_LANGUAGE_TAG
                             : keyboardLanguageTag;
@@ -328,4 +621,3 @@
                 || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEFAULT;
     }
 }
-
diff --git a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
index a1b67e1..f1698dd 100644
--- a/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/DefaultImeVisibilityApplier.java
@@ -37,6 +37,7 @@
 import android.util.EventLog;
 import android.util.Slog;
 import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
 
 import com.android.internal.annotations.GuardedBy;
@@ -75,7 +76,8 @@
     @GuardedBy("ImfLock.class")
     @Override
     public void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
-            int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         final IInputMethodInvoker curMethod = mService.getCurMethodLocked();
         if (curMethod != null) {
             if (DEBUG) {
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index c53f1a5..b12a816 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -30,6 +30,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ImeTracker;
 import android.view.inputmethod.InputBinding;
+import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodSubtype;
 import android.window.ImeOnBackInvokedDispatcher;
 
@@ -198,8 +199,8 @@
 
     // TODO(b/192412909): Convert this back to void method
     @AnyThread
-    boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken, int flags,
-            ResultReceiver resultReceiver) {
+    boolean showSoftInput(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
+            @InputMethod.ShowFlags int flags, ResultReceiver resultReceiver) {
         try {
             mTarget.showSoftInput(showInputToken, statsToken, flags, resultReceiver);
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
index 27f6a89..29fa369 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityApplier.java
@@ -21,6 +21,7 @@
 import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethod;
 
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 
@@ -34,13 +35,13 @@
      *
      * @param showInputToken A token that represents the requester to show IME.
      * @param statsToken     A token that tracks the progress of an IME request.
-     * @param showFlags      Provides additional operating flags to show IME.
      * @param resultReceiver If non-null, this will be called back to the caller when
      *                       it has processed request to tell what it has done.
      * @param reason         The reason for requesting to show IME.
      */
     default void performShowIme(IBinder showInputToken, @Nullable ImeTracker.Token statsToken,
-            int showFlags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {}
+            @InputMethod.ShowFlags int showFlags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {}
 
     /**
      * Performs hiding IME to the given window
diff --git a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
index f012d91..9ad4628 100644
--- a/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
+++ b/services/core/java/com/android/server/inputmethod/ImeVisibilityStateComputer.java
@@ -221,17 +221,21 @@
 
     /**
      * Called when {@link InputMethodManagerService} is processing the show IME request.
-     * @param statsToken The token for tracking this show request
-     * @param showFlags The additional operation flags to indicate whether this show request mode is
-     *                  implicit or explicit.
-     * @return {@code true} when the computer has proceed this show request operation.
+     *
+     * @param statsToken The token for tracking this show request.
+     * @return {@code true} when the show request can proceed.
      */
-    boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken, int showFlags) {
+    boolean onImeShowFlags(@NonNull ImeTracker.Token statsToken,
+            @InputMethodManager.ShowFlags int showFlags) {
         if (mPolicy.mA11yRequestingNoSoftKeyboard || mPolicy.mImeHiddenByDisplayPolicy) {
             ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
             return false;
         }
         ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_ACCESSIBILITY);
+        // We only "set" the state corresponding to the flags, as this will be reset
+        // in clearImeShowFlags during a hide request.
+        // Thus, we keep the strongest values set (e.g. an implicit show right after
+        // an explicit show will still be considered explicit, likewise for forced).
         if ((showFlags & InputMethodManager.SHOW_FORCED) != 0) {
             mRequestedShowExplicitly = true;
             mShowForced = true;
@@ -243,12 +247,12 @@
 
     /**
      * Called when {@link InputMethodManagerService} is processing the hide IME request.
-     * @param statsToken The token for tracking this hide request
-     * @param hideFlags The additional operation flags to indicate whether this hide request mode is
-     *                  implicit or explicit.
-     * @return {@code true} when the computer has proceed this hide request operations.
+     *
+     * @param statsToken The token for tracking this hide request.
+     * @return {@code true} when the hide request can proceed.
      */
-    boolean canHideIme(@NonNull ImeTracker.Token statsToken, int hideFlags) {
+    boolean canHideIme(@NonNull ImeTracker.Token statsToken,
+            @InputMethodManager.HideFlags int hideFlags) {
         if ((hideFlags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mRequestedShowExplicitly || mShowForced)) {
             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
@@ -264,13 +268,31 @@
         return true;
     }
 
-    int getImeShowFlags() {
+    /**
+     * Returns the show flags for IME. This translates from {@link InputMethodManager.ShowFlags}
+     * to {@link InputMethod.ShowFlags}.
+     */
+    @InputMethod.ShowFlags
+    int getShowFlagsForInputMethodServiceOnly() {
         int flags = 0;
         if (mShowForced) {
             flags |= InputMethod.SHOW_FORCED | InputMethod.SHOW_EXPLICIT;
         } else if (mRequestedShowExplicitly) {
             flags |= InputMethod.SHOW_EXPLICIT;
-        } else {
+        }
+        return flags;
+    }
+
+    /**
+     * Returns the show flags for IMM. This translates from {@link InputMethod.ShowFlags}
+     * to {@link InputMethodManager.ShowFlags}.
+     */
+    @InputMethodManager.ShowFlags
+    int getShowFlags() {
+        int flags = 0;
+        if (mShowForced) {
+            flags |= InputMethodManager.SHOW_FORCED;
+        } else if (!mRequestedShowExplicitly) {
             flags |= InputMethodManager.SHOW_IMPLICIT;
         }
         return flags;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 7bda2c1..c5fbcb9 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2468,7 +2468,7 @@
             final ImeTracker.Token statsToken = mCurStatsToken;
             mCurStatsToken = null;
             showCurrentInputLocked(mCurFocusedWindow, statsToken,
-                    mVisibilityStateComputer.getImeShowFlags(),
+                    mVisibilityStateComputer.getShowFlags(),
                     null /* resultReceiver */, SoftInputShowHideReason.ATTACH_NEW_INPUT);
         }
 
@@ -3404,8 +3404,9 @@
 
     @Override
     public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
-            @Nullable ImeTracker.Token statsToken, int flags, int lastClickTooType,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+            int lastClickTooType, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
         int uid = Binder.getCallingUid();
         ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3578,15 +3579,17 @@
 
     @GuardedBy("ImfLock.class")
     boolean showCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
-            int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            @InputMethodManager.ShowFlags int flags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         return showCurrentInputLocked(windowToken, statsToken, flags,
                 MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
     }
 
     @GuardedBy("ImfLock.class")
     private boolean showCurrentInputLocked(IBinder windowToken,
-            @Nullable ImeTracker.Token statsToken, int flags, int lastClickToolType,
-            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+            int lastClickToolType, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         // Create statsToken is none exists.
         if (statsToken == null) {
             statsToken = createStatsTokenForFocusedClient(true /* show */,
@@ -3617,7 +3620,8 @@
                 curMethod.updateEditorToolType(lastClickToolType);
             }
             mVisibilityApplier.performShowIme(windowToken, statsToken,
-                    mVisibilityStateComputer.getImeShowFlags(), resultReceiver, reason);
+                    mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
+                    resultReceiver, reason);
             mVisibilityStateComputer.setInputShown(true);
             return true;
         } else {
@@ -3629,8 +3633,8 @@
 
     @Override
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
-            @Nullable ImeTracker.Token statsToken, int flags, ResultReceiver resultReceiver,
-            @SoftInputShowHideReason int reason) {
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
         int uid = Binder.getCallingUid();
         ImeTracing.getInstance().triggerManagerServiceDump(
                 "InputMethodManagerService#hideSoftInput");
@@ -3660,7 +3664,8 @@
 
     @GuardedBy("ImfLock.class")
     boolean hideCurrentInputLocked(IBinder windowToken, @Nullable ImeTracker.Token statsToken,
-            int flags, ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+            @InputMethodManager.HideFlags int flags, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason) {
         // Create statsToken is none exists.
         if (statsToken == null) {
             statsToken = createStatsTokenForFocusedClient(false /* show */,
@@ -4847,7 +4852,7 @@
     }
 
     @BinderThread
-    private void hideMySoftInput(@NonNull IBinder token, int flags,
+    private void hideMySoftInput(@NonNull IBinder token, @InputMethodManager.HideFlags int flags,
             @SoftInputShowHideReason int reason) {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.hideMySoftInput");
@@ -4869,7 +4874,7 @@
     }
 
     @BinderThread
-    private void showMySoftInput(@NonNull IBinder token, int flags) {
+    private void showMySoftInput(@NonNull IBinder token, @InputMethodManager.ShowFlags int flags) {
         try {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showMySoftInput");
             synchronized (ImfLock.class) {
@@ -6828,8 +6833,8 @@
 
         @BinderThread
         @Override
-        public void hideMySoftInput(int flags, @SoftInputShowHideReason int reason,
-                AndroidFuture future /* T=Void */) {
+        public void hideMySoftInput(@InputMethodManager.HideFlags int flags,
+                @SoftInputShowHideReason int reason, AndroidFuture future /* T=Void */) {
             @SuppressWarnings("unchecked")
             final AndroidFuture<Void> typedFuture = future;
             try {
@@ -6842,7 +6847,8 @@
 
         @BinderThread
         @Override
-        public void showMySoftInput(int flags, AndroidFuture future /* T=Void */) {
+        public void showMySoftInput(@InputMethodManager.ShowFlags int flags,
+                AndroidFuture future /* T=Void */) {
             @SuppressWarnings("unchecked")
             final AndroidFuture<Void> typedFuture = future;
             try {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ecb21d0..b3ef869 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -118,7 +118,6 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Slog;
@@ -836,9 +835,6 @@
 
     @Override // binder interface
     public void systemReady() {
-        if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
-            EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), "");  // SafetyNet
-        }
         checkWritePermission();
 
         mHasSecureLockScreen = mContext.getPackageManager()
@@ -1093,9 +1089,6 @@
     }
 
     private final void checkPasswordHavePermission() {
-        if (mContext.checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
-            EventLog.writeEvent(0x534e4554, "28251513", getCallingUid(), "");  // SafetyNet
-        }
         mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave");
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2db724f..9be86e8 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -118,7 +118,6 @@
 import static android.service.notification.NotificationListenerService.TRIM_LIGHT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
 import static com.android.internal.util.FrameworkStatsLog.DND_MODE_RULE;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
@@ -1221,8 +1220,7 @@
                 }
             }
 
-            int mustNotHaveFlags = mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)
-                    ? FLAG_NO_DISMISS : FLAG_ONGOING_EVENT;
+            int mustNotHaveFlags = FLAG_NO_DISMISS;
             cancelNotification(callingUid, callingPid, pkg, tag, id,
                     /* mustHaveFlags= */ 0,
                     /* mustNotHaveFlags= */ mustNotHaveFlags,
@@ -2491,16 +2489,6 @@
         getContext().registerReceiver(mReviewNotificationPermissionsReceiver,
                 ReviewNotificationPermissionsReceiver.getFilter(),
                 Context.RECEIVER_NOT_EXPORTED);
-
-        mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
-                new AppOpsManager.OnOpChangedInternalListener() {
-                    @Override
-                    public void onOpChanged(@NonNull String op, @NonNull String packageName,
-                            int userId) {
-                        mHandler.post(
-                                () -> handleNotificationPermissionChange(packageName, userId));
-                    }
-                });
     }
 
     /**
@@ -3560,16 +3548,20 @@
             }
             mPermissionHelper.setNotificationPermission(
                     pkg, UserHandle.getUserId(uid), enabled, true);
+            sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
 
             mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
                     .setType(MetricsEvent.TYPE_ACTION)
                     .setPackageName(pkg)
                     .setSubtype(enabled ? 1 : 0));
             mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
+            // Now, cancel any outstanding notifications that are part of a just-disabled app
+            if (!enabled) {
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0,
+                        UserHandle.getUserId(uid), REASON_PACKAGE_BANNED);
+            }
 
-            // Outstanding notifications from this package will be cancelled, and the package will
-            // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the
-            // callback from AppOpsManager.
+            handleSavePolicyFile();
         }
 
         /**
@@ -5889,21 +5881,6 @@
         }
     };
 
-    private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) {
-        int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId);
-        if (uid == INVALID_UID) {
-            Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId));
-            return;
-        }
-        boolean hasPermission = mPermissionHelper.hasPermission(uid);
-        sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission);
-        if (!hasPermission) {
-            cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null,
-                    /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId,
-                    REASON_PACKAGE_BANNED);
-        }
-    }
-
     protected void checkNotificationListenerAccess() {
         if (!isCallerSystemOrPhone()) {
             getContext().enforceCallingPermission(
@@ -6883,13 +6860,11 @@
         }
 
         // Only notifications that can be non-dismissible can have the flag FLAG_NO_DISMISS
-        if (mFlagResolver.isEnabled(ALLOW_DISMISS_ONGOING)) {
-            if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
-                    && canBeNonDismissible(ai, notification)) {
-                notification.flags |= FLAG_NO_DISMISS;
-            } else {
-                notification.flags &= ~FLAG_NO_DISMISS;
-            }
+        if (((notification.flags & FLAG_ONGOING_EVENT) > 0)
+                && canBeNonDismissible(ai, notification)) {
+            notification.flags |= FLAG_NO_DISMISS;
+        } else {
+            notification.flags &= ~FLAG_NO_DISMISS;
         }
 
         int canColorize = getContext().checkPermission(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 36a0b0c..1f5bd3e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -75,6 +75,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.logging.MetricsLogger;
@@ -108,30 +109,34 @@
     static final int RULE_LIMIT_PER_PACKAGE = 100;
 
     // pkg|userId => uid
-    protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+    @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
 
     private final Context mContext;
     private final H mHandler;
     private final SettingsObserver mSettingsObserver;
     private final AppOpsManager mAppOps;
-    @VisibleForTesting protected final NotificationManager mNotificationManager;
+    private final NotificationManager mNotificationManager;
     private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
-    @VisibleForTesting protected ZenModeConfig mDefaultConfig;
+    private ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
     private final ZenModeFiltering mFiltering;
-    protected final RingerModeDelegate mRingerModeDelegate = new
+    private final RingerModeDelegate mRingerModeDelegate = new
             RingerModeDelegate();
     @VisibleForTesting protected final ZenModeConditions mConditions;
-    Object mConfigsLock = new Object();
+    private final Object mConfigsArrayLock = new Object();
+    @GuardedBy("mConfigsArrayLock")
     @VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
     private final Metrics mMetrics = new Metrics();
     private final ConditionProviders.Config mServiceConfig;
-    private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
-    @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
+    private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+    private final ZenModeEventLogger mZenModeEventLogger;
 
     @VisibleForTesting protected int mZenMode;
     @VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
     private int mUser = UserHandle.USER_SYSTEM;
+
+    private final Object mConfigLock = new Object();
+    @GuardedBy("mConfigLock")
     @VisibleForTesting protected ZenModeConfig mConfig;
     @VisibleForTesting protected AudioManagerInternal mAudioManager;
     protected PackageManager mPm;
@@ -159,7 +164,7 @@
         mDefaultConfig = readDefaultConfig(mContext.getResources());
         updateDefaultAutomaticRuleNames();
         mConfig = mDefaultConfig.copy();
-        synchronized (mConfigsLock) {
+        synchronized (mConfigsArrayLock) {
             mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
         }
         mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -186,7 +191,7 @@
     public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
             ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity,
             int callingUid) {
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
                     userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity,
                     callingUid);
@@ -206,7 +211,7 @@
     }
 
     public boolean shouldIntercept(NotificationRecord record) {
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
         }
     }
@@ -221,7 +226,7 @@
 
     public void initZenMode() {
         if (DEBUG) Log.d(TAG, "initZenMode");
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             // "update" config to itself, which will have no effect in the case where a config
             // was read in via XML, but will initialize zen mode if nothing was read in and the
             // config remains the default.
@@ -250,7 +255,7 @@
     public void onUserRemoved(int user) {
         if (user < UserHandle.USER_SYSTEM) return;
         if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
-        synchronized (mConfigsLock) {
+        synchronized (mConfigsArrayLock) {
             mConfigs.remove(user);
         }
     }
@@ -268,7 +273,7 @@
         mUser = user;
         if (DEBUG) Log.d(TAG, reason + " u=" + user);
         ZenModeConfig config = null;
-        synchronized (mConfigsLock) {
+        synchronized (mConfigsArrayLock) {
             if (mConfigs.get(user) != null) {
                 config = mConfigs.get(user).copy();
             }
@@ -278,7 +283,7 @@
             config = mDefaultConfig.copy();
             config.user = user;
         }
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
         }
         cleanUpZenRules();
@@ -314,7 +319,7 @@
 
     public List<ZenRule> getZenRules() {
         List<ZenRule> rules = new ArrayList<>();
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return rules;
             for (ZenRule rule : mConfig.automaticRules.values()) {
                 if (canManageAutomaticZenRule(rule)) {
@@ -327,7 +332,7 @@
 
     public AutomaticZenRule getAutomaticZenRule(String id) {
         ZenRule rule;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return null;
              rule = mConfig.automaticRules.get(id);
         }
@@ -364,7 +369,7 @@
         }
 
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) {
                 throw new AndroidRuntimeException("Could not create rule");
             }
@@ -387,7 +392,7 @@
     public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
             String reason, int callingUid, boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return false;
             if (DEBUG) {
                 Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
@@ -419,7 +424,7 @@
     public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
             boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return false;
             newConfig = mConfig.copy();
             ZenRule ruleToRemove = newConfig.automaticRules.get(id);
@@ -450,7 +455,7 @@
     public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
             boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return false;
             newConfig = mConfig.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -467,7 +472,7 @@
     public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
             boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return;
 
             newConfig = mConfig.copy();
@@ -481,7 +486,7 @@
     public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
             boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return;
             newConfig = mConfig.copy();
 
@@ -491,6 +496,7 @@
         }
     }
 
+    @GuardedBy("mConfigLock")
     private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
             Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
         if (rules == null || rules.isEmpty()) return;
@@ -538,7 +544,7 @@
             return 0;
         }
         int count = 0;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             for (ZenRule rule : mConfig.automaticRules.values()) {
                 if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
                     count++;
@@ -555,7 +561,7 @@
             return 0;
         }
         int count = 0;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             for (ZenRule rule : mConfig.automaticRules.values()) {
                 if (pkg.equals(rule.getPkg())) {
                     count++;
@@ -588,19 +594,23 @@
 
     protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
         updateDefaultAutomaticRuleNames();
-        for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
-            ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
-            // if default rule wasn't user-modified nor enabled, use localized name
-            // instead of previous system name
-            if (currRule != null && !currRule.modified && !currRule.enabled
-                    && !defaultRule.name.equals(currRule.name)) {
-                if (canManageAutomaticZenRule(currRule)) {
-                    if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
-                            + "from " + currRule.name + " to " + defaultRule.name);
-                    // update default rule (if locale changed, name of rule will change)
-                    currRule.name = defaultRule.name;
-                    updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
-                            "locale changed", callingUid, fromSystemOrSystemUi);
+        synchronized (mConfigLock) {
+            for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+                ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+                // if default rule wasn't user-modified nor enabled, use localized name
+                // instead of previous system name
+                if (currRule != null && !currRule.modified && !currRule.enabled
+                        && !defaultRule.name.equals(currRule.name)) {
+                    if (canManageAutomaticZenRule(currRule)) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "Locale change - updating default zen rule name "
+                                    + "from " + currRule.name + " to " + defaultRule.name);
+                        }
+                        // update default rule (if locale changed, name of rule will change)
+                        currRule.name = defaultRule.name;
+                        updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+                                "locale changed", callingUid, fromSystemOrSystemUi);
+                    }
                 }
             }
         }
@@ -686,7 +696,7 @@
     private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
             boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
         ZenModeConfig newConfig;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig == null) return;
             if (!Global.isValidZenMode(zenMode)) return;
             if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
@@ -715,7 +725,7 @@
 
     void dump(ProtoOutputStream proto) {
         proto.write(ZenModeProto.ZEN_MODE, mZenMode);
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             if (mConfig.manualRule != null) {
                 mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
             }
@@ -737,14 +747,14 @@
         pw.println(Global.zenModeToString(mZenMode));
         pw.print(prefix);
         pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
-        synchronized(mConfigsLock) {
+        synchronized (mConfigsArrayLock) {
             final int N = mConfigs.size();
             for (int i = 0; i < N; i++) {
                 dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
             }
         }
         pw.print(prefix); pw.print("mUser="); pw.println(mUser);
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             dump(pw, prefix, "mConfig", mConfig);
         }
 
@@ -833,7 +843,7 @@
                         Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
             }
             if (DEBUG) Log.d(TAG, reason);
-            synchronized (mConfig) {
+            synchronized (mConfigLock) {
                 setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
             }
         }
@@ -841,7 +851,7 @@
 
     public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
             throws IOException {
-        synchronized (mConfigsLock) {
+        synchronized (mConfigsArrayLock) {
             final int n = mConfigs.size();
             for (int i = 0; i < n; i++) {
                 if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -856,7 +866,9 @@
      * @return user-specified default notification policy for priority only do not disturb
      */
     public Policy getNotificationPolicy() {
-        return getNotificationPolicy(mConfig);
+        synchronized (mConfigLock) {
+            return getNotificationPolicy(mConfig);
+        }
     }
 
     private static Policy getNotificationPolicy(ZenModeConfig config) {
@@ -867,8 +879,8 @@
      * Sets the global notification policy used for priority only do not disturb
      */
     public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
-        if (policy == null || mConfig == null) return;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
+            if (policy == null || mConfig == null) return;
             final ZenModeConfig newConfig = mConfig.copy();
             newConfig.applyNotificationPolicy(policy);
             setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
@@ -881,7 +893,7 @@
      */
     private void cleanUpZenRules() {
         long currentTime = System.currentTimeMillis();
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             final ZenModeConfig newConfig = mConfig.copy();
             if (newConfig.automaticRules != null) {
                 for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -906,7 +918,7 @@
      * @return a copy of the zen mode configuration
      */
     public ZenModeConfig getConfig() {
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             return mConfig.copy();
         }
     }
@@ -918,7 +930,8 @@
         return mConsolidatedPolicy.copy();
     }
 
-    public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+    @GuardedBy("mConfigLock")
+    private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
             String reason, int callingUid, boolean fromSystemOrSystemUi) {
         return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
                 callingUid, fromSystemOrSystemUi);
@@ -926,11 +939,12 @@
 
     public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
             int callingUid, boolean fromSystemOrSystemUi) {
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
             setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
         }
     }
 
+    @GuardedBy("mConfigLock")
     private boolean setConfigLocked(ZenModeConfig config, String reason,
             ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
             boolean fromSystemOrSystemUi) {
@@ -942,7 +956,7 @@
             }
             if (config.user != mUser) {
                 // simply store away for background users
-                synchronized (mConfigsLock) {
+                synchronized (mConfigsArrayLock) {
                     mConfigs.put(config.user, config);
                 }
                 if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -951,7 +965,7 @@
             // handle CPS backed conditions - danger! may modify config
             mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
 
-            synchronized (mConfigsLock) {
+            synchronized (mConfigsArrayLock) {
                 mConfigs.put(config.user, config);
             }
             if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -979,6 +993,7 @@
      * Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
      * If logging is enabled, will also request logging of the outcome of this change if needed.
      */
+    @GuardedBy("mConfigLock")
     private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
             boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
         final boolean logZenModeEvents = mFlagResolver.isEnabled(
@@ -993,7 +1008,7 @@
         }
         final String val = Integer.toString(config.hashCode());
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
-        evaluateZenMode(reason, setRingerMode);
+        evaluateZenModeLocked(reason, setRingerMode);
         // After all changes have occurred, log if requested
         if (logZenModeEvents) {
             ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
@@ -1025,7 +1040,8 @@
     }
 
     @VisibleForTesting
-    protected void evaluateZenMode(String reason, boolean setRingerMode) {
+    @GuardedBy("mConfigLock")
+    protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
         if (DEBUG) Log.d(TAG, "evaluateZenMode");
         if (mConfig == null) return;
         final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1056,8 +1072,8 @@
     }
 
     private int computeZenMode() {
-        if (mConfig == null) return Global.ZEN_MODE_OFF;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
+            if (mConfig == null) return Global.ZEN_MODE_OFF;
             if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
             int zen = Global.ZEN_MODE_OFF;
             for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -1094,8 +1110,8 @@
     }
 
     private void updateConsolidatedPolicy(String reason) {
-        if (mConfig == null) return;
-        synchronized (mConfig) {
+        synchronized (mConfigLock) {
+            if (mConfig == null) return;
             ZenPolicy policy = new ZenPolicy();
             if (mConfig.manualRule != null) {
                 applyCustomPolicy(policy, mConfig.manualRule);
@@ -1293,7 +1309,7 @@
      * Generate pulled atoms about do not disturb configurations.
      */
     public void pullRules(List<StatsEvent> events) {
-        synchronized (mConfigsLock) {
+        synchronized (mConfigsArrayLock) {
             final int numConfigs = mConfigs.size();
             for (int i = 0; i < numConfigs; i++) {
                 final int user = mConfigs.keyAt(i);
@@ -1319,6 +1335,7 @@
         }
     }
 
+    @GuardedBy("mConfigsArrayLock")
     private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
             List<StatsEvent> events) {
         // Make the ID safe.
@@ -1389,7 +1406,7 @@
 
             if (mZenMode == Global.ZEN_MODE_OFF
                     || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                    && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
+                    && !areAllPriorityOnlyRingerSoundsMuted())) {
                 // in priority only with ringer not muted, save ringer mode changes
                 // in dnd off, save ringer mode changes
                 setPreviousRingerModeSetting(ringerModeNew);
@@ -1410,8 +1427,7 @@
                             && (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
                             || mZenMode == Global.ZEN_MODE_ALARMS
                             || (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
-                            && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
-                            mConfig)))) {
+                            && areAllPriorityOnlyRingerSoundsMuted()))) {
                         newZen = Global.ZEN_MODE_OFF;
                     } else if (mZenMode != Global.ZEN_MODE_OFF) {
                         ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
@@ -1430,6 +1446,12 @@
             return ringerModeExternalOut;
         }
 
+        private boolean areAllPriorityOnlyRingerSoundsMuted() {
+            synchronized (mConfigLock) {
+                return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig);
+            }
+        }
+
         @Override
         public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
                 int ringerModeInternal, VolumePolicy policy) {
@@ -1633,7 +1655,7 @@
         private void emitRules() {
             final long now = SystemClock.elapsedRealtime();
             final long since = (now - mRuleCountLogTime);
-            synchronized (mConfig) {
+            synchronized (mConfigLock) {
                 int numZenRules = mConfig.automaticRules.size();
                 if (mNumZenRules != numZenRules
                         || since > MINIMUM_LOG_PERIOD_MS) {
@@ -1651,7 +1673,7 @@
         private void emitDndType() {
             final long now = SystemClock.elapsedRealtime();
             final long since = (now - mTypeLogTimeMs);
-            synchronized (mConfig) {
+            synchronized (mConfigLock) {
                 boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
                 int zenType = !dndOn ? DND_OFF
                         : (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 2206eac..26e70c0 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -356,9 +356,13 @@
     /**
      * Performs a non-staged install of the given {@code apexFile}.
      *
+     * If {@code force} is {@code true}, then  update is forced even for APEXes that do not support
+     * non-staged update. This feature is only available on debuggable builds to improve development
+     * velocity of the teams that have their code packaged in an APEX.
+     *
      * @return {@code ApeInfo} about the newly installed APEX package.
      */
-    abstract ApexInfo installPackage(File apexFile) throws PackageManagerException;
+    abstract ApexInfo installPackage(File apexFile, boolean force) throws PackageManagerException;
 
     /**
      * Get a list of apex system services implemented in an apex.
@@ -903,10 +907,11 @@
         }
 
         @Override
-        ApexInfo installPackage(File apexFile)
+        ApexInfo installPackage(File apexFile, boolean force)
                 throws PackageManagerException {
             try {
-                return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath());
+                return waitForApexService().installAndActivatePackage(apexFile.getAbsolutePath(),
+                        force);
             } catch (RemoteException e) {
                 throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                         "apexservice not available");
diff --git a/services/core/java/com/android/server/pm/ArchiveManager.java b/services/core/java/com/android/server/pm/ArchiveManager.java
index 99479f0..5435206 100644
--- a/services/core/java/com/android/server/pm/ArchiveManager.java
+++ b/services/core/java/com/android/server/pm/ArchiveManager.java
@@ -77,9 +77,8 @@
         snapshot.enforceCrossUserPermission(callingUid, userId, true, true,
                 "archiveApp");
         verifyCaller(callerPackageName, callingPackageName);
-
         PackageStateInternal ps = getPackageState(packageName, snapshot, callingUid, user);
-        verifyInstallOwnership(packageName, callingPackageName, ps.getInstallSource());
+        verifyInstaller(packageName, ps.getInstallSource());
 
         List<LauncherActivityInfo> mainActivities = getLauncherApps().getActivityList(
                 ps.getPackageName(),
@@ -125,7 +124,7 @@
                     Path.of("/TODO"), null);
             activityInfos.add(activityInfo);
         }
-        // TODO(b/278553670) Adapt installer check verifyInstallOwnership and check for null there
+
         InstallSource installSource = ps.getInstallSource();
         String installerPackageName = installSource.mUpdateOwnerPackageName != null
                 ? installSource.mUpdateOwnerPackageName : installSource.mInstallerPackageName;
@@ -159,19 +158,13 @@
         }
     }
 
-    private static void verifyInstallOwnership(String packageName, String callingPackageName,
-            InstallSource installSource) {
-        if (!TextUtils.equals(installSource.mInstallerPackageName,
-                callingPackageName)) {
+    private static void verifyInstaller(String packageName, InstallSource installSource) {
+        // TODO(b/291060290) Verify installer supports unarchiving
+        if (installSource.mUpdateOwnerPackageName == null
+                && installSource.mInstallerPackageName == null) {
             throw new SecurityException(
-                    TextUtils.formatSimple("Caller is not the installer of record for %s.",
+                    TextUtils.formatSimple("No installer found to archive app %s.",
                             packageName));
         }
-        String updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
-        if (updateOwnerPackageName != null
-                && !TextUtils.equals(updateOwnerPackageName, callingPackageName)) {
-            throw new SecurityException(
-                    TextUtils.formatSimple("Caller is not the update owner for %s.", packageName));
-        }
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 6de7f07..dd96a2b 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -43,6 +43,7 @@
     final IPackageInstallObserver2 mObserver;
     // Always refers to PackageManager flags only
     final int mInstallFlags;
+    final int mDevelopmentInstallFlags;
     @NonNull
     final InstallSource mInstallSource;
     final String mVolumeUuid;
@@ -69,8 +70,8 @@
     @Nullable String[] mInstructionSets;
 
     InstallArgs(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
-            int installFlags, InstallSource installSource, String volumeUuid,
-            UserHandle user, String[] instructionSets, String abiOverride,
+            int installFlags, int developmentInstallFlags, InstallSource installSource,
+            String volumeUuid,  UserHandle user, String[] instructionSets, String abiOverride,
             @NonNull ArrayMap<String, Integer> permissionStates,
             List<String> allowlistedRestrictedPermissions,
             int autoRevokePermissionsMode, String traceMethod, int traceCookie,
@@ -80,6 +81,7 @@
         mOriginInfo = originInfo;
         mMoveInfo = moveInfo;
         mInstallFlags = installFlags;
+        mDevelopmentInstallFlags = developmentInstallFlags;
         mObserver = observer;
         mInstallSource = Preconditions.checkNotNull(installSource);
         mVolumeUuid = volumeUuid;
@@ -105,7 +107,7 @@
      * when cleaning up old installs, or used as a move source.
      */
     InstallArgs(String codePath, String[] instructionSets) {
-        this(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY, null, null,
+        this(OriginInfo.fromNothing(), null, null, 0, 0, InstallSource.EMPTY, null, null,
                 instructionSets, null, new ArrayMap<>(), null, MODE_DEFAULT, null, 0,
                 SigningDetails.UNKNOWN, PackageManager.INSTALL_REASON_UNKNOWN,
                 PackageManager.INSTALL_SCENARIO_DEFAULT, false, DataLoaderType.NONE,
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d737cc4..134b041 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -110,6 +110,7 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.DataLoaderType;
@@ -119,7 +120,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
-import android.content.pm.ResolveInfo;
 import android.content.pm.SharedLibraryInfo;
 import android.content.pm.Signature;
 import android.content.pm.SigningDetails;
@@ -184,7 +184,9 @@
 import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.pkg.SharedLibraryWrapper;
 import com.android.server.pm.pkg.component.ComponentMutateUtils;
+import com.android.server.pm.pkg.component.ParsedActivity;
 import com.android.server.pm.pkg.component.ParsedInstrumentation;
+import com.android.server.pm.pkg.component.ParsedIntentInfo;
 import com.android.server.pm.pkg.component.ParsedPermission;
 import com.android.server.pm.pkg.component.ParsedPermissionGroup;
 import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
@@ -207,6 +209,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -3936,23 +3939,6 @@
             }
         }
 
-        // If this is a system app we hadn't seen before, and this is a first boot or OTA,
-        // we need to unstop it if it doesn't have a launcher entry.
-        if (mPm.mShouldStopSystemPackagesByDefault && scanResult.mRequest.mPkgSetting == null
-                && ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0)
-                && ((scanFlags & SCAN_AS_SYSTEM) != 0)) {
-            final Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
-            launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-            launcherIntent.setPackage(parsedPackage.getPackageName());
-            final List<ResolveInfo> launcherActivities =
-                    mPm.snapshotComputer().queryIntentActivitiesInternal(launcherIntent, null,
-                            PackageManager.MATCH_DIRECT_BOOT_AWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
-            if (launcherActivities.isEmpty()) {
-                scanResult.mPkgSetting.setStopped(false, 0);
-            }
-        }
-
         if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
             if (scanResult.mPkgSetting != null && scanResult.mPkgSetting.isLoading()) {
                 // Continue monitoring loading progress of active incremental packages
@@ -4325,6 +4311,8 @@
         //   - It's an APEX or overlay package since stopped state does not affect them.
         //   - It is enumerated with a <initial-package-state> tag having the stopped attribute
         //     set to false
+        //   - It doesn't have an enabled and exported launcher activity, which means the user
+        //     wouldn't have a way to un-stop it
         final boolean isApexPkg = (scanFlags & SCAN_AS_APEX) != 0;
         if (mPm.mShouldStopSystemPackagesByDefault
                 && scanSystemPartition
@@ -4333,8 +4321,9 @@
                 && !parsedPackage.isOverlayIsStatic()
         ) {
             String packageName = parsedPackage.getPackageName();
-            if (!mPm.mInitialNonStoppedSystemPackages.contains(packageName)
-                    && !"android".contentEquals(packageName)) {
+            if (!"android".contentEquals(packageName)
+                    && !mPm.mInitialNonStoppedSystemPackages.contains(packageName)
+                    && hasLauncherEntry(parsedPackage)) {
                 scanFlags |= SCAN_AS_STOPPED_SYSTEM_APP;
             }
         }
@@ -4344,6 +4333,26 @@
         return new Pair<>(scanResult, shouldHideSystemApp);
     }
 
+    private static boolean hasLauncherEntry(ParsedPackage parsedPackage) {
+        final HashSet<String> categories = new HashSet<>();
+        categories.add(Intent.CATEGORY_LAUNCHER);
+        final List<ParsedActivity> activities = parsedPackage.getActivities();
+        for (int indexActivity = 0; indexActivity < activities.size(); indexActivity++) {
+            final ParsedActivity activity = activities.get(indexActivity);
+            if (!activity.isEnabled() || !activity.isExported()) {
+                continue;
+            }
+            final List<ParsedIntentInfo> intents = activity.getIntents();
+            for (int indexIntent = 0; indexIntent < intents.size(); indexIntent++) {
+                final IntentFilter intentFilter = intents.get(indexIntent).getIntentFilter();
+                if (intentFilter != null && intentFilter.matchCategories(categories) == null) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Returns if forced apk verification can be skipped for the whole package, including splits.
      */
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 3464874..6fc14e8 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -134,12 +134,13 @@
     InstallRequest(InstallingSession params) {
         mUserId = params.getUser().getIdentifier();
         mInstallArgs = new InstallArgs(params.mOriginInfo, params.mMoveInfo, params.mObserver,
-                params.mInstallFlags, params.mInstallSource, params.mVolumeUuid,
-                params.getUser(), null /*instructionSets*/, params.mPackageAbiOverride,
-                params.mPermissionStates, params.mAllowlistedRestrictedPermissions,
-                params.mAutoRevokePermissionsMode, params.mTraceMethod, params.mTraceCookie,
-                params.mSigningDetails, params.mInstallReason, params.mInstallScenario,
-                params.mForceQueryableOverride, params.mDataLoaderType, params.mPackageSource,
+                params.mInstallFlags, params.mDevelopmentInstallFlags, params.mInstallSource,
+                params.mVolumeUuid,  params.getUser(), null /*instructionSets*/,
+                params.mPackageAbiOverride, params.mPermissionStates,
+                params.mAllowlistedRestrictedPermissions, params.mAutoRevokePermissionsMode,
+                params.mTraceMethod, params.mTraceCookie, params.mSigningDetails,
+                params.mInstallReason, params.mInstallScenario, params.mForceQueryableOverride,
+                params.mDataLoaderType, params.mPackageSource,
                 params.mApplicationEnabledSettingPersistent);
         mPackageMetrics = new PackageMetrics(this);
         mIsInstallInherit = params.mIsInherit;
@@ -286,6 +287,10 @@
         return mInstallArgs == null ? 0 : mInstallArgs.mInstallFlags;
     }
 
+    public int getDevelopmentInstallFlags() {
+        return mInstallArgs == null ? 0 : mInstallArgs.mDevelopmentInstallFlags;
+    }
+
     public int getInstallReason() {
         return mInstallArgs == null ? INSTALL_REASON_UNKNOWN : mInstallArgs.mInstallReason;
     }
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index 35862db..30a23bf 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -68,6 +68,7 @@
     final MoveInfo mMoveInfo;
     final IPackageInstallObserver2 mObserver;
     int mInstallFlags;
+    int mDevelopmentInstallFlags;
     @NonNull
     final InstallSource mInstallSource;
     final String mVolumeUuid;
@@ -102,8 +103,8 @@
 
     // For move install
     InstallingSession(OriginInfo originInfo, MoveInfo moveInfo, IPackageInstallObserver2 observer,
-            int installFlags, InstallSource installSource, String volumeUuid,
-            UserHandle user, String packageAbiOverride, int packageSource,
+            int installFlags, int developmentInstallFlags, InstallSource installSource,
+            String volumeUuid, UserHandle user, String packageAbiOverride, int packageSource,
             PackageLite packageLite, PackageManagerService pm) {
         mPm = pm;
         mUser = user;
@@ -113,6 +114,7 @@
         mMoveInfo = moveInfo;
         mObserver = observer;
         mInstallFlags = installFlags;
+        mDevelopmentInstallFlags = developmentInstallFlags;
         mInstallSource = Preconditions.checkNotNull(installSource);
         mVolumeUuid = volumeUuid;
         mPackageAbiOverride = packageAbiOverride;
@@ -149,6 +151,7 @@
         mInstallScenario = sessionParams.installScenario;
         mObserver = observer;
         mInstallFlags = sessionParams.installFlags;
+        mDevelopmentInstallFlags = sessionParams.developmentInstallFlags;
         mInstallSource = installSource;
         mVolumeUuid = sessionParams.volumeUuid;
         mPackageAbiOverride = sessionParams.abiOverride;
@@ -592,6 +595,10 @@
                     "Only a non-staged install of a single APEX is supported");
         }
         InstallRequest request = requests.get(0);
+        boolean force =
+                (request.getDevelopmentInstallFlags()
+                        & PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE)
+                        != 0;
         try {
             // Should directory scanning logic be moved to ApexManager for better test coverage?
             final File dir = request.getOriginInfo().mResolvedFile;
@@ -608,7 +615,7 @@
                         PackageManagerException.INTERNAL_ERROR_APEX_MORE_THAN_ONE_FILE);
             }
             try (PackageParser2 packageParser = mPm.mInjector.getScanningPackageParser()) {
-                ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0]);
+                ApexInfo apexInfo = mPm.mApexManager.installPackage(apexes[0], force);
                 // APEX has been handled successfully by apexd. Let's continue the install flow
                 // so it will be scanned and registered with the system.
                 // TODO(b/225756739): Improve atomicity of rebootless APEX install.
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 15c10aa..1a5591c 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -302,8 +302,9 @@
                 new File(origin.mResolvedPath), /* flags */ 0);
         final PackageLite lite = ret.isSuccess() ? ret.getResult() : null;
         final InstallingSession installingSession = new InstallingSession(origin, move,
-                installObserver, installFlags, installSource, volumeUuid, user, packageAbiOverride,
-                PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED, lite, mPm);
+                installObserver, installFlags, /* developmentInstallFlags= */ 0, installSource,
+                volumeUuid, user, packageAbiOverride,  PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED,
+                lite, mPm);
         installingSession.movePackage();
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 2c0e74d..80e07f4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -709,6 +709,9 @@
                     != PackageManager.PERMISSION_GRANTED) {
                 params.installFlags &= ~PackageManager.INSTALL_ALLOW_TEST;
             }
+
+            // developmentInstallFlags can ony be set by shell or root.
+            params.developmentInstallFlags = 0;
         }
 
         String originatingPackageName = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9137d44..10fe65df 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2121,8 +2121,8 @@
                 // Save the names of pre-existing packages prior to scanning, so we can determine
                 // which system packages are completely new due to an upgrade.
                 mExistingPackages = new ArraySet<>(packageSettings.size());
-                for (PackageSetting ps : packageSettings.values()) {
-                    mExistingPackages.add(ps.getPackageName());
+                for (int i = 0; i < packageSettings.size(); i++) {
+                    mExistingPackages.add(packageSettings.valueAt(i).getPackageName());
                 }
 
                 // Triggering {@link com.android.server.pm.crossprofile.
@@ -2249,8 +2249,10 @@
             // If this is the first boot or an update from pre-M, then we need to initialize the
             // default preferred apps across all defined users.
             if (mPromoteSystemApps || mFirstBoot) {
-                for (UserInfo user : mInjector.getUserManagerInternal().getUsers(true)) {
-                    mSettings.applyDefaultPreferredAppsLPw(user.id);
+                final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(true);
+                for (int i = 0; i < users.size(); i++) {
+                    mSettings.applyDefaultPreferredAppsLPw(users.get(i).id);
+
                 }
             }
 
@@ -4278,8 +4280,9 @@
         final Computer snapshot = snapshotComputer();
         for (String packageName : apkList) {
             setSystemAppHiddenUntilInstalled(snapshot, packageName, true);
-            for (UserInfo user : mInjector.getUserManagerInternal().getUsers(false)) {
-                setSystemAppInstallState(snapshot, packageName, false, user.id);
+            final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(false);
+            for (int i = 0; i < users.size(); i++) {
+                setSystemAppInstallState(snapshot, packageName, false, users.get(i).id);
             }
         }
     }
@@ -4724,7 +4727,8 @@
 
                 ArraySet<CrossProfileIntentFilter> set =
                         new ArraySet<>(resolver.filterSet());
-                for (CrossProfileIntentFilter filter : set) {
+                for (int i = 0; i < set.size(); i++) {
+                    final CrossProfileIntentFilter filter = set.valueAt(i);
                     if (IntentFilter.filterEquals(filter.mFilter, intentFilter)
                             && filter.getOwnerPackage().equals(ownerPackage)
                             && filter.getTargetUserId() == targetUserId
@@ -6066,8 +6070,8 @@
             final Computer snapshot = snapshotComputer();
             enforceOwnerRights(snapshot, packageName, Binder.getCallingUid());
             mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
-            for (String mimeType : mimeTypes) {
-                if (mimeType.length() > 255) {
+            for (int i = 0; i < mimeTypes.size(); i++) {
+                if (mimeTypes.get(i).length() > 255) {
                     throw new IllegalArgumentException("MIME type length exceeds 255 characters");
                 }
             }
@@ -6941,7 +6945,9 @@
 
                 if (targetPkg.getLibraryNames() != null) {
                     // Set the overlay paths for dependencies of the shared library.
-                    for (final String libName : targetPkg.getLibraryNames()) {
+                    List<String> libraryNames = targetPkg.getLibraryNames();
+                    for (int j = 0; j < libraryNames.size(); j++) {
+                        final String libName = libraryNames.get(j);
                         ArraySet<String> modifiedDependents = null;
 
                         final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
@@ -6955,7 +6961,8 @@
                         if (dependents == null) {
                             continue;
                         }
-                        for (final VersionedPackage dependent : dependents) {
+                        for (int k = 0; k < dependents.size(); k++) {
+                            final VersionedPackage dependent = dependents.get(k);
                             final PackageStateInternal dependentState =
                                     computer.getPackageStateInternal(dependent.getPackageName());
                             if (dependentState == null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 8d64bd9..6eff241 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3308,6 +3308,13 @@
         // Set package source to other by default
         sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER);
 
+        // Encodes one of the states:
+        //  1. Install request explicitly specified --staged, then value will be true.
+        //  2. Install request explicitly specified --non-staged, then value will be false.
+        //  3. Install request did not specify either --staged or --non-staged, then for APEX
+        //      installs the value will be true, and for apk installs it will be false.
+        Boolean staged = null;
+
         String opt;
         boolean replaceExisting = true;
         boolean forceNonStaged = false;
@@ -3416,7 +3423,6 @@
                     break;
                 case "--apex":
                     sessionParams.setInstallAsApex();
-                    sessionParams.setStaged();
                     break;
                 case "--force-non-staged":
                     forceNonStaged = true;
@@ -3425,7 +3431,10 @@
                     sessionParams.setMultiPackage();
                     break;
                 case "--staged":
-                    sessionParams.setStaged();
+                    staged = true;
+                    break;
+                case "--non-staged":
+                    staged = false;
                     break;
                 case "--force-queryable":
                     sessionParams.setForceQueryable();
@@ -3460,11 +3469,18 @@
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
         }
+        if (staged == null) {
+            staged = (sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
+        }
         if (replaceExisting) {
             sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
         }
         if (forceNonStaged) {
             sessionParams.isStaged = false;
+            sessionParams.developmentInstallFlags |=
+                    PackageManager.INSTALL_DEVELOPMENT_FORCE_NON_STAGED_APEX_UPDATE;
+        } else if (staged) {
+            sessionParams.setStaged();
         }
         return params;
     }
@@ -4346,7 +4362,8 @@
         pw.println("       [--preload] [--instant] [--full] [--dont-kill]");
         pw.println("       [--enable-rollback]");
         pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
-        pw.println("       [--apex] [--force-non-staged] [--staged-ready-timeout TIMEOUT]");
+        pw.println("       [--apex] [--non-staged] [--force-non-staged]");
+        pw.println("       [--staged-ready-timeout TIMEOUT]");
         pw.println("       [PATH [SPLIT...]|-]");
         pw.println("    Install an application.  Must provide the apk data to install, either as");
         pw.println("    file path(s) or '-' to read from stdin.  Options are:");
@@ -4375,8 +4392,12 @@
         pw.println("      --update-ownership: request the update ownership enforcement");
         pw.println("      --force-uuid: force install on to disk volume with given UUID");
         pw.println("      --apex: install an .apex file, not an .apk");
+        pw.println("      --non-staged: explicitly set this installation to be non-staged.");
+        pw.println("          This flag is only useful for APEX installs that are implicitly");
+        pw.println("          assumed to be staged.");
         pw.println("      --force-non-staged: force the installation to run under a non-staged");
-        pw.println("          session, which may complete without requiring a reboot");
+        pw.println("          session, which may complete without requiring a reboot. This will");
+        pw.println("          force a rebootless update even for APEXes that don't support it");
         pw.println("      --staged-ready-timeout: By default, staged sessions wait "
                 + DEFAULT_STAGED_READY_TIMEOUT_MS);
         pw.println("          milliseconds for pre-reboot verification to complete when");
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 9b92697..494709c 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -1195,8 +1195,11 @@
     }
 
     public PackageSetting setLoadingProgress(float progress) {
-        mLoadingProgress = progress;
-        onChanged();
+        // To prevent race conditions, we don't allow progress to ever go down
+        if (mLoadingProgress < progress) {
+            mLoadingProgress = progress;
+            onChanged();
+        }
         return this;
     }
 
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 3c846da..5e23765 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -18,7 +18,12 @@
       "name": "CtsCompilationTestCases"
     },
     {
-      "name": "CtsAppEnumerationTestCases"
+      "name": "CtsAppEnumerationTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.LargeTest"
+        }
+      ]
     },
     {
       "name": "CtsMatchFlagTestCases"
@@ -129,6 +134,9 @@
     },
     {
       "name": "PackageManagerServiceHostTests"
+    },
+    {
+      "name": "CtsAppEnumerationTestCases"
     }
   ],
   "imports": [
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7609073..b01a89e 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1230,8 +1230,7 @@
             }
 
             if (!fromDatasource && !checkPermission(context, permissionManagerServiceInt,
-                    permission, attributionSource.getUid(),
-                    attributionSource.getRenouncedPermissions())) {
+                    permission, attributionSource)) {
                 return PermissionChecker.PERMISSION_HARD_DENIED;
             }
 
@@ -1292,12 +1291,11 @@
                     }
                     case AppOpsManager.MODE_DEFAULT: {
                         if (!skipCurrentChecks && !checkPermission(context,
-                                permissionManagerServiceInt, permission, attributionSource.getUid(),
-                                attributionSource.getRenouncedPermissions())) {
+                                permissionManagerServiceInt, permission, attributionSource)) {
                             return PermissionChecker.PERMISSION_HARD_DENIED;
                         }
                         if (next != null && !checkPermission(context, permissionManagerServiceInt,
-                                permission, next.getUid(), next.getRenouncedPermissions())) {
+                                permission, next)) {
                             return PermissionChecker.PERMISSION_HARD_DENIED;
                         }
                     }
@@ -1326,8 +1324,7 @@
             // We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
             // every attributionSource in the chain is registered with the system.
             final boolean isChainStartTrusted = !hasChain || checkPermission(context,
-                    permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
-                    current.getRenouncedPermissions());
+                    permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current);
 
             while (true) {
                 final boolean skipCurrentChecks = (fromDatasource || next != null);
@@ -1342,12 +1339,12 @@
 
                 // If we already checked the permission for this one, skip the work
                 if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt,
-                        permission, current.getUid(), current.getRenouncedPermissions())) {
+                        permission, current)) {
                     return PermissionChecker.PERMISSION_HARD_DENIED;
                 }
 
                 if (next != null && !checkPermission(context, permissionManagerServiceInt,
-                        permission, next.getUid(), next.getRenouncedPermissions())) {
+                        permission, next)) {
                     return PermissionChecker.PERMISSION_HARD_DENIED;
                 }
 
@@ -1415,9 +1412,13 @@
 
         private static boolean checkPermission(@NonNull Context context,
                 @NonNull PermissionManagerServiceInternal permissionManagerServiceInt,
-                @NonNull String permission, int uid, @NonNull Set<String> renouncedPermissions) {
-            boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1,
-                    uid) == PackageManager.PERMISSION_GRANTED;
+                @NonNull String permission, AttributionSource attributionSource) {
+            int uid = attributionSource.getUid();
+            int deviceId = attributionSource.getDeviceId();
+            final Context deviceContext = context.getDeviceId() == deviceId ? context
+                    : context.createDeviceContext(deviceId);
+            boolean permissionGranted = deviceContext.checkPermission(permission,
+                    Process.INVALID_PID, uid) == PackageManager.PERMISSION_GRANTED;
 
             // Override certain permissions checks for the shared isolated process for both
             // HotwordDetectionService and VisualQueryDetectionService, which ordinarily cannot hold
@@ -1433,10 +1434,10 @@
                 permissionGranted = hotwordServiceProvider != null
                         && uid == hotwordServiceProvider.getUid();
             }
-
+            Set<String> renouncedPermissions = attributionSource.getRenouncedPermissions();
             if (permissionGranted && renouncedPermissions.contains(permission)
-                    && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
-                    /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) {
+                    && deviceContext.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS,
+                        Process.INVALID_PID, uid) == PackageManager.PERMISSION_GRANTED) {
                 return false;
             }
             return permissionGranted;
@@ -1507,8 +1508,7 @@
             // We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and
             // every attributionSource in the chain is registered with the system.
             final boolean isChainStartTrusted = !hasChain || checkPermission(context,
-                    permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(),
-                    current.getRenouncedPermissions());
+                    permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current);
 
             while (true) {
                 final boolean skipCurrentChecks = (next != null);
diff --git a/services/core/java/com/android/server/policy/ModifierShortcutManager.java b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
index 44cc3e7..539bb6b 100644
--- a/services/core/java/com/android/server/policy/ModifierShortcutManager.java
+++ b/services/core/java/com/android/server/policy/ModifierShortcutManager.java
@@ -16,12 +16,15 @@
 
 package com.android.server.policy;
 
+import android.annotation.SuppressLint;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.res.XmlResourceParser;
+import android.hardware.input.InputManager;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -29,11 +32,14 @@
 import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
 import com.android.internal.policy.IShortcutService;
 import com.android.internal.util.XmlUtils;
+import com.android.server.input.KeyboardMetricsCollector;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -87,11 +93,13 @@
     }
 
     private final Context mContext;
+    private final Handler mHandler;
     private boolean mSearchKeyShortcutPending = false;
     private boolean mConsumeSearchKeyUp = true;
 
-    ModifierShortcutManager(Context context) {
+    ModifierShortcutManager(Context context, Handler handler) {
         mContext = context;
+        mHandler = handler;
         loadShortcuts();
     }
 
@@ -267,11 +275,13 @@
      * Handle the shortcut to {@link Intent}
      *
      * @param kcm the {@link KeyCharacterMap} associated with the keyboard device.
-     * @param keyCode The key code of the event.
+     * @param keyEvent The key event.
      * @param metaState The meta key modifier state.
      * @return True if invoked the shortcut, otherwise false.
      */
-    private boolean handleIntentShortcut(KeyCharacterMap kcm, int keyCode, int metaState) {
+    @SuppressLint("MissingPermission")
+    private boolean handleIntentShortcut(KeyCharacterMap kcm, KeyEvent keyEvent, int metaState) {
+        final int keyCode = keyEvent.getKeyCode();
         // Shortcuts are invoked through Search+key, so intercept those here
         // Any printing key that is chorded with Search should be consumed
         // even if no shortcut was invoked.  This prevents text from being
@@ -301,6 +311,7 @@
                             + "keyCode=" + KeyEvent.keyCodeToString(keyCode) + ","
                             + " category=" + category);
                 }
+                logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(intent));
                 return true;
             } else {
                 return false;
@@ -317,11 +328,24 @@
                         + "the activity to which it is registered was not found: "
                         + "META+ or SEARCH" + KeyEvent.keyCodeToString(keyCode));
             }
+            logKeyboardShortcut(keyEvent, KeyboardLogEvent.getLogEventFromIntent(shortcutIntent));
             return true;
         }
         return false;
     }
 
+    private void logKeyboardShortcut(KeyEvent event, KeyboardLogEvent logEvent) {
+        mHandler.post(() -> handleKeyboardLogging(event, logEvent));
+    }
+
+    private void handleKeyboardLogging(KeyEvent event, KeyboardLogEvent logEvent) {
+        final InputManager inputManager = mContext.getSystemService(InputManager.class);
+        final InputDevice inputDevice = inputManager != null
+                ? inputManager.getInputDevice(event.getDeviceId()) : null;
+        KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice,
+                logEvent, event.getMetaState(), event.getKeyCode());
+    }
+
     /**
      * Handle the shortcut from {@link KeyEvent}
      *
@@ -354,7 +378,7 @@
         }
 
         final KeyCharacterMap kcm = event.getKeyCharacterMap();
-        if (handleIntentShortcut(kcm, keyCode, metaState)) {
+        if (handleIntentShortcut(kcm, event, metaState)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6e441bf..7f86f1d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.internal.policy.TransitionAnimation;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.AccessibilityManagerInternal;
@@ -221,6 +220,7 @@
 import com.android.server.display.BrightnessUtils;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.input.KeyboardMetricsCollector;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
@@ -228,6 +228,7 @@
 import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
 import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
 import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.vibrator.HapticFeedbackVibrationProvider;
 import com.android.server.vr.VrManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -371,12 +372,6 @@
     private static final String TALKBACK_LABEL = "TalkBack";
 
     private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
-    private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
-            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
-    private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
-            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
-    private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
-            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
 
     /**
      * Keyguard stuff
@@ -425,6 +420,7 @@
     ActivityManagerInternal mActivityManagerInternal;
     ActivityTaskManagerInternal mActivityTaskManagerInternal;
     AutofillManagerInternal mAutofillManagerInternal;
+    InputManager mInputManager;
     InputManagerInternal mInputManagerInternal;
     DreamManagerInternal mDreamManagerInternal;
     PowerManagerInternal mPowerManagerInternal;
@@ -450,6 +446,7 @@
     PackageManager mPackageManager;
     SideFpsEventHandler mSideFpsEventHandler;
     LockPatternUtils mLockPatternUtils;
+    private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
     private boolean mHasFeatureAuto;
     private boolean mHasFeatureWatch;
     private boolean mHasFeatureLeanback;
@@ -458,9 +455,6 @@
     // Assigned on main thread, accessed on UI thread
     volatile VrManagerInternal mVrManagerInternal;
 
-    // Vibrator pattern for haptic feedback during boot when safe mode is enabled.
-    long[] mSafeModeEnabledVibePattern;
-
     /** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
     boolean mEnableShiftMenuBugReports = false;
 
@@ -558,7 +552,6 @@
     int mPowerVolUpBehavior;
     boolean mStylusButtonsEnabled = true;
     boolean mHasSoftInput = false;
-    boolean mHapticTextHandleEnabled;
     boolean mUseTvRouting;
     boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
     MetricsLogger mLogger;
@@ -778,7 +771,7 @@
                     handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
                     break;
                 case MSG_LOG_KEYBOARD_SYSTEM_EVENT:
-                    handleKeyboardSystemEvent(msg.arg2, (KeyEvent) msg.obj);
+                    handleKeyboardSystemEvent(KeyboardLogEvent.from(msg.arg1), (KeyEvent) msg.obj);
                     break;
             }
         }
@@ -1837,8 +1830,11 @@
     }
 
     private void launchAllAppsViaA11y() {
-        getAccessibilityManagerInternal().performSystemAction(
-                AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+        AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
+        if (accessibilityManager != null) {
+            accessibilityManager.performSystemAction(
+                    AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+        }
     }
 
     private void toggleNotificationPanel() {
@@ -1909,6 +1905,7 @@
             // If we have released the home key, and didn't do anything else
             // while it was pressed, then it is time to go home!
             if (!down) {
+                logKeyboardSystemsEvent(event, KeyboardLogEvent.HOME);
                 if (mDisplayId == DEFAULT_DISPLAY) {
                     cancelPreloadRecentApps();
                 }
@@ -2110,6 +2107,7 @@
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+        mInputManager = mContext.getSystemService(InputManager.class);
         mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
@@ -2179,7 +2177,7 @@
         mWakeGestureListener = new MyWakeGestureListener(mContext, mHandler);
         mSettingsObserver = new SettingsObserver(mHandler);
         mSettingsObserver.observe();
-        mModifierShortcutManager = new ModifierShortcutManager(mContext);
+        mModifierShortcutManager = new ModifierShortcutManager(mContext, mHandler);
         mUiMode = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_defaultUiModeType);
         mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
@@ -2251,9 +2249,6 @@
         mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
 
-        mHapticTextHandleEnabled = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_enableHapticTextHandle);
-
         mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
 
         mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
@@ -2294,8 +2289,8 @@
         mContext.registerReceiver(mMultiuserReceiver, filter);
 
         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
-        mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
-                com.android.internal.R.array.config_safeModeEnabledVibePattern);
+        mHapticFeedbackVibrationProvider =
+                new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator);
 
         mGlobalKeyManager = new GlobalKeyManager(mContext);
 
@@ -3023,20 +3018,33 @@
      * We won't log keyboard events when the input device is null
      * or when it is virtual.
      */
-    private void handleKeyboardSystemEvent(int keyboardSystemEvent, KeyEvent event) {
-        final InputManager inputManager = mContext.getSystemService(InputManager.class);
-        final InputDevice inputDevice = inputManager != null
-                ? inputManager.getInputDevice(event.getDeviceId()) : null;
-        if (inputDevice != null && !inputDevice.isVirtual()) {
-            KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(
-                    inputDevice, keyboardSystemEvent,
-                    new int[]{event.getKeyCode()}, event.getMetaState());
-        }
+    private void handleKeyboardSystemEvent(KeyboardLogEvent keyboardLogEvent, KeyEvent event) {
+        final InputDevice inputDevice = mInputManager.getInputDevice(event.getDeviceId());
+        KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom(inputDevice,
+                keyboardLogEvent, event.getMetaState(), event.getKeyCode());
+        event.recycle();
     }
 
-    private void logKeyboardSystemsEvent(KeyEvent event, int keyboardSystemEvent) {
-        mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, 0, keyboardSystemEvent, event)
-                .sendToTarget();
+    private void logKeyboardSystemsEventOnActionUp(KeyEvent event,
+            KeyboardLogEvent keyboardSystemEvent) {
+        if (event.getAction() != KeyEvent.ACTION_UP) {
+            return;
+        }
+        logKeyboardSystemsEvent(event, keyboardSystemEvent);
+    }
+
+    private void logKeyboardSystemsEventOnActionDown(KeyEvent event,
+            KeyboardLogEvent keyboardSystemEvent) {
+        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+            return;
+        }
+        logKeyboardSystemsEvent(event, keyboardSystemEvent);
+    }
+
+    private void logKeyboardSystemsEvent(KeyEvent event, KeyboardLogEvent keyboardSystemEvent) {
+        KeyEvent eventToLog = KeyEvent.obtain(event);
+        mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, keyboardSystemEvent.getIntValue(), 0,
+                eventToLog).sendToTarget();
     }
 
     // TODO(b/117479243): handle it in InputPolicy
@@ -3137,8 +3145,6 @@
 
         switch (keyCode) {
             case KeyEvent.KEYCODE_HOME:
-                logKeyboardSystemsEvent(event,
-                        FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME);
                 return handleHomeShortcuts(displayId, focusedToken, event);
             case KeyEvent.KEYCODE_MENU:
                 // Hijack modified menu keys for debugging features
@@ -3149,14 +3155,14 @@
                     Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
                     mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
                             null, null, null, 0, null, null);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.TRIGGER_BUG_REPORT);
                     return true;
                 }
                 break;
             case KeyEvent.KEYCODE_RECENT_APPS:
                 if (firstDown) {
                     showRecentApps(false /* triggeredFromAltTab */);
-                    logKeyboardSystemsEvent(event,
-                            FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
                 }
                 return true;
             case KeyEvent.KEYCODE_APP_SWITCH:
@@ -3165,6 +3171,7 @@
                         preloadRecentApps();
                     } else if (!down) {
                         toggleRecentApps();
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.APP_SWITCH);
                     }
                 }
                 return true;
@@ -3173,6 +3180,7 @@
                     launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
                             deviceId, event.getEventTime(),
                             AssistUtils.INVOCATION_TYPE_UNKNOWN);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
                     return true;
                 }
                 break;
@@ -3185,12 +3193,14 @@
             case KeyEvent.KEYCODE_I:
                 if (firstDown && event.isMetaPressed()) {
                     showSystemSettings();
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS);
                     return true;
                 }
                 break;
             case KeyEvent.KEYCODE_L:
                 if (firstDown && event.isMetaPressed()) {
                     lockNow(null /* options */);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LOCK_SCREEN);
                     return true;
                 }
                 break;
@@ -3198,8 +3208,10 @@
                 if (firstDown && event.isMetaPressed()) {
                     if (event.isCtrlPressed()) {
                         sendSystemKeyToStatusBarAsync(event);
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_NOTES);
                     } else {
                         toggleNotificationPanel();
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
                     }
                     return true;
                 }
@@ -3207,12 +3219,14 @@
             case KeyEvent.KEYCODE_S:
                 if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
                     interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.TAKE_SCREENSHOT);
                     return true;
                 }
                 break;
             case KeyEvent.KEYCODE_T:
                 if (firstDown && event.isMetaPressed()) {
                     toggleTaskbar();
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_TASKBAR);
                     return true;
                 }
                 break;
@@ -3221,6 +3235,7 @@
                     StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                     if (statusbar != null) {
                         statusbar.goToFullscreenFromSplit();
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
                         return true;
                     }
                 }
@@ -3228,18 +3243,21 @@
             case KeyEvent.KEYCODE_DPAD_LEFT:
                 if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
                     enterStageSplitFromRunningApp(true /* leftOrTop */);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
                     return true;
                 }
                 break;
             case KeyEvent.KEYCODE_DPAD_RIGHT:
                 if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
                     enterStageSplitFromRunningApp(false /* leftOrTop */);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION);
                     return true;
                 }
                 break;
             case KeyEvent.KEYCODE_SLASH:
                 if (firstDown && event.isMetaPressed() && !keyguardOn) {
                     toggleKeyboardShortcutsMenu(event.getDeviceId());
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.OPEN_SHORTCUT_HELPER);
                     return true;
                 }
                 break;
@@ -3308,20 +3326,26 @@
                     intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
                     intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
                     startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.getBrightnessEvent(keyCode));
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
                 if (down) {
                     mInputManagerInternal.decrementKeyboardBacklight(event.getDeviceId());
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
                 if (down) {
                     mInputManagerInternal.incrementKeyboardBacklight(event.getDeviceId());
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP);
                 }
                 return true;
             case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
                 // TODO: Add logic
+                if (!down) {
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE);
+                }
                 return true;
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -3347,6 +3371,7 @@
                 if (firstDown && !keyguardOn && isUserSetupComplete()) {
                     if (event.isMetaPressed()) {
                         showRecentApps(false);
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
                         return true;
                     } else if (mRecentAppsHeldModifiers == 0) {
                         final int shiftlessModifiers =
@@ -3355,6 +3380,7 @@
                                 shiftlessModifiers, KeyEvent.META_ALT_ON)) {
                             mRecentAppsHeldModifiers = shiftlessModifiers;
                             showRecentApps(true);
+                            logKeyboardSystemsEvent(event, KeyboardLogEvent.RECENT_APPS);
                             return true;
                         }
                     }
@@ -3366,11 +3392,13 @@
                     Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.ALL_APPS);
                 }
                 return true;
             case KeyEvent.KEYCODE_NOTIFICATION:
                 if (!down) {
                     toggleNotificationPanel();
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL);
                 }
                 return true;
             case KeyEvent.KEYCODE_SEARCH:
@@ -3378,6 +3406,7 @@
                     switch (mSearchKeyBehavior) {
                         case SEARCH_BEHAVIOR_TARGET_ACTIVITY: {
                             launchTargetSearchActivity();
+                            logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_SEARCH);
                             return true;
                         }
                         case SEARCH_BEHAVIOR_DEFAULT_SEARCH:
@@ -3390,6 +3419,7 @@
                 if (firstDown) {
                     int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
                     sendSwitchKeyboardLayout(event, direction);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
                     return true;
                 }
                 break;
@@ -3398,6 +3428,7 @@
                 if (firstDown && event.isMetaPressed()) {
                     int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
                     sendSwitchKeyboardLayout(event, direction);
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LANGUAGE_SWITCH);
                     return true;
                 }
                 break;
@@ -3416,9 +3447,11 @@
                     if (mPendingCapsLockToggle) {
                         mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                         mPendingCapsLockToggle = false;
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
                     } else if (mPendingMetaAction) {
                         if (!canceled) {
                             launchAllAppsViaA11y();
+                            logKeyboardSystemsEvent(event, KeyboardLogEvent.ACCESSIBILITY_ALL_APPS);
                         }
                         mPendingMetaAction = false;
                     }
@@ -3446,10 +3479,16 @@
                     if (mPendingCapsLockToggle) {
                         mInputManagerInternal.toggleCapsLock(event.getDeviceId());
                         mPendingCapsLockToggle = false;
+                        logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
                         return true;
                     }
                 }
                 break;
+            case KeyEvent.KEYCODE_CAPS_LOCK:
+                if (!down) {
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.TOGGLE_CAPS_LOCK);
+                }
+                break;
             case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
@@ -4289,6 +4328,7 @@
         // Handle special keys.
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.BACK);
                 if (down) {
                     mBackKeyHandled = false;
                 } else {
@@ -4306,6 +4346,8 @@
             case KeyEvent.KEYCODE_VOLUME_DOWN:
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
+                logKeyboardSystemsEventOnActionDown(event,
+                        KeyboardLogEvent.getVolumeEvent(keyCode));
                 if (down) {
                     sendSystemKeyToStatusBarAsync(event);
 
@@ -4406,6 +4448,7 @@
             }
 
             case KeyEvent.KEYCODE_TV_POWER: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false; // wake-up will be handled separately
                 if (down && hdmiControlManager != null) {
@@ -4415,6 +4458,7 @@
             }
 
             case KeyEvent.KEYCODE_POWER: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.TOGGLE_POWER);
                 EventLogTags.writeInterceptPower(
                         KeyEvent.actionToString(event.getAction()),
                         mPowerKeyHandled ? 1 : 0,
@@ -4437,12 +4481,14 @@
             case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT:
                 // fall through
             case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SYSTEM_NAVIGATION);
                 result &= ~ACTION_PASS_TO_USER;
                 interceptSystemNavigationKey(event);
                 break;
             }
 
             case KeyEvent.KEYCODE_SLEEP: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false;
                 if (!mPowerManager.isInteractive()) {
@@ -4457,6 +4503,7 @@
             }
 
             case KeyEvent.KEYCODE_SOFT_SLEEP: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.SLEEP);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = false;
                 if (!down) {
@@ -4466,6 +4513,7 @@
             }
 
             case KeyEvent.KEYCODE_WAKEUP: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.WAKEUP);
                 result &= ~ACTION_PASS_TO_USER;
                 isWakeKey = true;
                 break;
@@ -4474,6 +4522,7 @@
             case KeyEvent.KEYCODE_MUTE:
                 result &= ~ACTION_PASS_TO_USER;
                 if (down && event.getRepeatCount() == 0) {
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.SYSTEM_MUTE);
                     toggleMicrophoneMuteFromKey();
                 }
                 break;
@@ -4488,6 +4537,7 @@
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
             case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
+                logKeyboardSystemsEventOnActionUp(event, KeyboardLogEvent.MEDIA_KEY);
                 if (MediaSessionLegacyHelper.getHelper(mContext).isGlobalPriorityActive()) {
                     // If the global session is active pass all media keys to it
                     // instead of the active window.
@@ -4532,6 +4582,7 @@
                             0 /* unused */, event.getEventTime() /* eventTime */);
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_ASSISTANT);
                 }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
@@ -4542,6 +4593,7 @@
                     Message msg = mHandler.obtainMessage(MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK);
                     msg.setAsynchronous(true);
                     msg.sendToTarget();
+                    logKeyboardSystemsEvent(event, KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT);
                 }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
@@ -5499,10 +5551,6 @@
         }
     }
 
-    static long[] getLongIntArray(Resources r, int resid) {
-        return ArrayUtils.convertToLongArray(r.getIntArray(resid));
-    }
-
     private void bindKeyguard() {
         synchronized (mLock) {
             if (mKeyguardBound) {
@@ -5989,138 +6037,18 @@
         if (!mVibrator.hasVibrator()) {
             return false;
         }
-        VibrationEffect effect = getVibrationEffect(effectId);
+        VibrationEffect effect =
+                mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId);
         if (effect == null) {
             return false;
         }
-        VibrationAttributes attrs = getVibrationAttributes(effectId);
-        if (always) {
-            attrs = new VibrationAttributes.Builder(attrs)
-                    .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
-                    .build();
-        }
+        VibrationAttributes attrs =
+                mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
+                        effectId, /* bypassVibrationIntensitySetting= */ always);
         mVibrator.vibrate(uid, packageName, effect, reason, attrs);
         return true;
     }
 
-    private VibrationEffect getVibrationEffect(int effectId) {
-        long[] pattern;
-        switch (effectId) {
-            case HapticFeedbackConstants.CONTEXT_CLICK:
-            case HapticFeedbackConstants.GESTURE_END:
-            case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
-            case HapticFeedbackConstants.SCROLL_TICK:
-            case HapticFeedbackConstants.SEGMENT_TICK:
-                return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
-
-            case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
-                if (!mHapticTextHandleEnabled) {
-                    return null;
-                }
-                // fallthrough
-            case HapticFeedbackConstants.CLOCK_TICK:
-            case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
-                return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
-
-            case HapticFeedbackConstants.KEYBOARD_RELEASE:
-            case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
-            case HapticFeedbackConstants.ENTRY_BUMP:
-            case HapticFeedbackConstants.DRAG_CROSSING:
-                return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
-
-            case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
-            case HapticFeedbackConstants.VIRTUAL_KEY:
-            case HapticFeedbackConstants.EDGE_RELEASE:
-            case HapticFeedbackConstants.CALENDAR_DATE:
-            case HapticFeedbackConstants.CONFIRM:
-            case HapticFeedbackConstants.GESTURE_START:
-            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
-            case HapticFeedbackConstants.SCROLL_LIMIT:
-                return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-
-            case HapticFeedbackConstants.LONG_PRESS:
-            case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
-            case HapticFeedbackConstants.DRAG_START:
-            case HapticFeedbackConstants.EDGE_SQUEEZE:
-                return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
-            case HapticFeedbackConstants.REJECT:
-                return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
-
-            case HapticFeedbackConstants.SAFE_MODE_ENABLED:
-                pattern = mSafeModeEnabledVibePattern;
-                break;
-
-            case HapticFeedbackConstants.ASSISTANT_BUTTON:
-                if (mVibrator.areAllPrimitivesSupported(
-                        VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
-                        VibrationEffect.Composition.PRIMITIVE_TICK)) {
-                    // quiet ramp, short pause, then sharp tick
-                    return VibrationEffect.startComposition()
-                            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
-                            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
-                            .compose();
-                }
-                // fallback for devices without composition support
-                return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
-            case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
-                return getScaledPrimitiveOrElseEffect(
-                        VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
-                        VibrationEffect.EFFECT_TEXTURE_TICK);
-
-            case HapticFeedbackConstants.TOGGLE_ON:
-                return getScaledPrimitiveOrElseEffect(
-                        VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
-                        VibrationEffect.EFFECT_TICK);
-
-            case HapticFeedbackConstants.TOGGLE_OFF:
-                return getScaledPrimitiveOrElseEffect(
-                        VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
-                        VibrationEffect.EFFECT_TEXTURE_TICK);
-
-            case HapticFeedbackConstants.NO_HAPTICS:
-            default:
-                return null;
-        }
-        if (pattern.length == 0) {
-            // No vibration
-            return null;
-        } else if (pattern.length == 1) {
-            // One-shot vibration
-            return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
-        } else {
-            // Pattern vibration
-            return VibrationEffect.createWaveform(pattern, -1);
-        }
-    }
-
-    private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale,
-            int elseEffectId) {
-        if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
-            return VibrationEffect.startComposition()
-                    .addPrimitive(primitiveId, scale)
-                    .compose();
-        } else {
-            return VibrationEffect.get(elseEffectId);
-        }
-    }
-
-    private VibrationAttributes getVibrationAttributes(int effectId) {
-        switch (effectId) {
-            case HapticFeedbackConstants.EDGE_SQUEEZE:
-            case HapticFeedbackConstants.EDGE_RELEASE:
-                return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
-            case HapticFeedbackConstants.ASSISTANT_BUTTON:
-            case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
-            case HapticFeedbackConstants.SCROLL_TICK:
-            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
-            case HapticFeedbackConstants.SCROLL_LIMIT:
-                return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
-            default:
-                return TOUCH_VIBRATION_ATTRIBUTES;
-        }
-    }
 
     @Override
     public void keepScreenOnStartedLw() {
@@ -6258,8 +6186,7 @@
                 pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
                 pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
         pw.print(prefix);
-                pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
-                pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+                pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
         pw.print(prefix);
                 pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
                 pw.print(" mIncallPowerBehavior=");
@@ -6284,6 +6211,7 @@
                 pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
                 pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
 
+        mHapticFeedbackVibrationProvider.dump(prefix, pw);
         mGlobalKeyManager.dump(prefix, pw);
         mKeyCombinationManager.dump(prefix, pw);
         mSingleKeyGestureDetector.dump(prefix, pw);
@@ -6545,6 +6473,7 @@
 
     private class HdmiVideoExtconUEventObserver extends ExtconStateObserver<Boolean> {
         private static final String HDMI_EXIST = "HDMI=1";
+        private static final String DP_EXIST = "DP=1";
         private static final String NAME = "hdmi";
 
         private boolean init(ExtconInfo hdmi) {
@@ -6575,7 +6504,8 @@
         public Boolean parseState(ExtconInfo extconIfno, String state) {
             // extcon event state changes from kernel4.9
             // new state will be like STATE=HDMI=1
-            return state.contains(HDMI_EXIST);
+            // or like STATE=DP=1 for newer kernel
+            return state.contains(HDMI_EXIST) || state.contains(DP_EXIST);
         }
     }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fc971e4..5a050ac 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -92,6 +92,7 @@
 import android.os.UserManager;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
+import android.provider.DeviceConfigInterface;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
@@ -128,6 +129,7 @@
 import com.android.server.UserspaceRebootLogger;
 import com.android.server.Watchdog;
 import com.android.server.am.BatteryStatsService;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LogicalLight;
 import com.android.server.policy.WindowManagerPolicy;
@@ -323,6 +325,9 @@
     private final Injector mInjector;
     private final PermissionCheckerWrapper mPermissionCheckerWrapper;
     private final PowerPropertiesWrapper mPowerPropertiesWrapper;
+    private final DeviceConfigParameterProvider mDeviceConfigProvider;
+
+    private boolean mDisableScreenWakeLocksWhileCached;
 
     private LightsManager mLightsManager;
     private BatteryManagerInternal mBatteryManagerInternal;
@@ -1065,6 +1070,10 @@
                 }
             };
         }
+
+        DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
+            return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
+        }
     }
 
     /** Interface for checking an app op permission */
@@ -1161,6 +1170,7 @@
                 mInjector.createInattentiveSleepWarningController();
         mPermissionCheckerWrapper = mInjector.createPermissionCheckerWrapper();
         mPowerPropertiesWrapper = mInjector.createPowerPropertiesWrapper();
+        mDeviceConfigProvider = mInjector.createDeviceConfigParameterProvider();
 
         mPowerGroupWakefulnessChangeListener = new PowerGroupWakefulnessChangeListener();
 
@@ -1346,6 +1356,14 @@
 
             mLightsManager = getLocalService(LightsManager.class);
             mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+            updateDeviceConfigLocked();
+            mDeviceConfigProvider.addOnPropertiesChangedListener(BackgroundThread.getExecutor(),
+                    properties -> {
+                        synchronized (mLock) {
+                            updateDeviceConfigLocked();
+                            updateWakeLockDisabledStatesLocked();
+                        }
+                    });
 
             // Initialize display power management.
             mDisplayManagerInternal.initPowerManagement(
@@ -1545,6 +1563,12 @@
         updatePowerStateLocked();
     }
 
+    @GuardedBy("mLock")
+    private void updateDeviceConfigLocked() {
+        mDisableScreenWakeLocksWhileCached = mDeviceConfigProvider
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+    }
+
     @RequiresPermission(value = android.Manifest.permission.TURN_SCREEN_ON, conditional = true)
     private void acquireWakeLockInternal(IBinder lock, int displayId, int flags, String tag,
             String packageName, WorkSource ws, String historyTag, int uid, int pid,
@@ -2760,13 +2784,13 @@
     /** Get wake lock summary flags that correspond to the given wake lock. */
     @SuppressWarnings("deprecation")
     private int getWakeLockSummaryFlags(WakeLock wakeLock) {
+        if (wakeLock.mDisabled) {
+            // We only respect this if the wake lock is not disabled.
+            return 0;
+        }
         switch (wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK) {
             case PowerManager.PARTIAL_WAKE_LOCK:
-                if (!wakeLock.mDisabled) {
-                    // We only respect this if the wake lock is not disabled.
-                    return WAKE_LOCK_CPU;
-                }
-                break;
+                return WAKE_LOCK_CPU;
             case PowerManager.FULL_WAKE_LOCK:
                 return WAKE_LOCK_SCREEN_BRIGHT | WAKE_LOCK_BUTTON_BRIGHT;
             case PowerManager.SCREEN_BRIGHT_WAKE_LOCK:
@@ -4151,7 +4175,7 @@
         for (int i = 0; i < numWakeLocks; i++) {
             final WakeLock wakeLock = mWakeLocks.get(i);
             if ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
-                    == PowerManager.PARTIAL_WAKE_LOCK) {
+                    == PowerManager.PARTIAL_WAKE_LOCK || isScreenLock(wakeLock)) {
                 if (setWakeLockDisabledStateLocked(wakeLock)) {
                     changed = true;
                     if (wakeLock.mDisabled) {
@@ -4205,6 +4229,22 @@
                 }
             }
             return wakeLock.setDisabled(disabled);
+        } else if (mDisableScreenWakeLocksWhileCached && isScreenLock(wakeLock)) {
+            boolean disabled = false;
+            final int appid = UserHandle.getAppId(wakeLock.mOwnerUid);
+            final UidState state = wakeLock.mUidState;
+            // Cached inactive processes are never allowed to hold wake locks.
+            if (mConstants.NO_CACHED_WAKE_LOCKS
+                    && appid >= Process.FIRST_APPLICATION_UID
+                    && !state.mActive
+                    && state.mProcState != ActivityManager.PROCESS_STATE_NONEXISTENT
+                    && state.mProcState >= ActivityManager.PROCESS_STATE_TOP_SLEEPING) {
+                if (DEBUG_SPEW) {
+                    Slog.d(TAG, "disabling full wakelock " + wakeLock);
+                }
+                disabled = true;
+            }
+            return wakeLock.setDisabled(disabled);
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
index d8e6c26..d1d27f3 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsDataStorage.java
@@ -17,6 +17,7 @@
 package com.android.server.powerstats;
 
 import android.content.Context;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.internal.util.FileRotator;
@@ -27,6 +28,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
+import java.util.Date;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
@@ -266,4 +268,51 @@
             mLock.unlock();
         }
     }
+
+    /**
+     * Dump stats about stored data.
+     */
+    public void dump(IndentingPrintWriter ipw) {
+        mLock.lock();
+        try {
+            final int versionDot = mDataStorageFilename.lastIndexOf('.');
+            final String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
+            final File[] files = mDataStorageDir.listFiles();
+
+            int number = 0;
+            int dataSize = 0;
+            long earliestLogEpochTime = Long.MAX_VALUE;
+            for (int i = 0; i < files.length; i++) {
+                // Check that the stems before the version match.
+                final File file = files[i];
+                final String fileName = file.getName();
+                if (files[i].getName().startsWith(beforeVersionDot)) {
+                    number++;
+                    dataSize += file.length();
+                    final int firstTimeChar = fileName.lastIndexOf('.') + 1;
+                    final int endChar = fileName.lastIndexOf('-');
+                    try {
+                        final Long startTime =
+                                Long.parseLong(fileName.substring(firstTimeChar, endChar));
+                        if (startTime != null && startTime < earliestLogEpochTime) {
+                            earliestLogEpochTime = startTime;
+                        }
+                    } catch (NumberFormatException nfe) {
+                        Slog.e(TAG,
+                            "Failed to extract start time from file : " + fileName, nfe);
+                    }
+                }
+            }
+
+            if (earliestLogEpochTime != Long.MAX_VALUE) {
+                ipw.println("Earliest data time : " + new Date(earliestLogEpochTime));
+            } else {
+                ipw.println("Failed to parse earliest data time!!!");
+            }
+            ipw.println("# files : " + number);
+            ipw.println("Total data size (B) : " + dataSize);
+        } finally {
+            mLock.unlock();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
index 39ead13..e80a86d 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java
@@ -30,6 +30,7 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.AtomicFile;
+import android.util.IndentingPrintWriter;
 import android.util.Slog;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
@@ -354,4 +355,23 @@
             updateCacheFile(residencyCacheFilename, powerEntityBytes);
         }
     }
+
+    /**
+     * Dump stats about stored data.
+     */
+    public void dump(IndentingPrintWriter ipw) {
+        ipw.println("PowerStats Meter Data:");
+        ipw.increaseIndent();
+        mPowerStatsMeterStorage.dump(ipw);
+        ipw.decreaseIndent();
+        ipw.println("PowerStats Model Data:");
+        ipw.increaseIndent();
+        mPowerStatsModelStorage.dump(ipw);
+        ipw.decreaseIndent();
+        ipw.println("PowerStats State Residency Data:");
+        ipw.increaseIndent();
+        mPowerStatsResidencyStorage.dump(ipw);
+        ipw.decreaseIndent();
+    }
+
 }
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java
index 9832c49..5609f69 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsService.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java
@@ -42,6 +42,7 @@
 import android.power.PowerStatsInternal;
 import android.provider.DeviceConfig;
 import android.provider.DeviceConfigInterface;
+import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.Slog;
 
@@ -232,18 +233,31 @@
                     } else if ("residency".equals(args[1])) {
                         mPowerStatsLogger.writeResidencyDataToFile(fd);
                     }
-                } else if (args.length == 0) {
-                    pw.println("PowerStatsService dumpsys: available PowerEntities");
+                } else {
+                    IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+                    ipw.println("PowerStatsService dumpsys: available PowerEntities");
                     PowerEntity[] powerEntity = getPowerStatsHal().getPowerEntityInfo();
-                    PowerEntityUtils.dumpsys(powerEntity, pw);
+                    ipw.increaseIndent();
+                    PowerEntityUtils.dumpsys(powerEntity, ipw);
+                    ipw.decreaseIndent();
 
-                    pw.println("PowerStatsService dumpsys: available Channels");
+                    ipw.println("PowerStatsService dumpsys: available Channels");
                     Channel[] channel = getPowerStatsHal().getEnergyMeterInfo();
-                    ChannelUtils.dumpsys(channel, pw);
+                    ipw.increaseIndent();
+                    ChannelUtils.dumpsys(channel, ipw);
+                    ipw.decreaseIndent();
 
-                    pw.println("PowerStatsService dumpsys: available EnergyConsumers");
+                    ipw.println("PowerStatsService dumpsys: available EnergyConsumers");
                     EnergyConsumer[] energyConsumer = getPowerStatsHal().getEnergyConsumerInfo();
-                    EnergyConsumerUtils.dumpsys(energyConsumer, pw);
+                    ipw.increaseIndent();
+                    EnergyConsumerUtils.dumpsys(energyConsumer, ipw);
+                    ipw.decreaseIndent();
+
+                    ipw.println("PowerStatsService dumpsys: PowerStatsLogger stats");
+                    ipw.increaseIndent();
+                    mPowerStatsLogger.dump(ipw);
+                    ipw.decreaseIndent();
+
                 }
             }
         }
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
new file mode 100644
index 0000000..8be3b2d
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackCustomization.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationEffect;
+import android.os.vibrator.persistence.VibrationXmlParser;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.vibrator.persistence.XmlParserException;
+import com.android.internal.vibrator.persistence.XmlReader;
+import com.android.internal.vibrator.persistence.XmlValidator;
+import com.android.modules.utils.TypedXmlPullParser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * Class that loads custom {@link VibrationEffect} to be performed for each
+ * {@link HapticFeedbackConstants} key.
+ *
+ * <p>The system has its default logic to get the {@link VibrationEffect} that will be played for a
+ * given haptic feedback constant. Devices may choose to override some or all of these supported
+ * haptic feedback vibrations via a customization XML.
+ *
+ * <p>The XML simply provides a mapping of a constant from {@link HapticFeedbackConstants} to its
+ * corresponding {@link VibrationEffect}. Its root tag should be `<haptic-feedback-constants>`. It
+ * should have one or more entries for customizing a haptic feedback constant. A customization is
+ * started by a `<constant id="X">` tag (where `X` is the haptic feedback constant being customized
+ * in this entry) and closed by </constant>. Between these two tags, there should be a valid XML
+ * serialization of a non-repeating {@link VibrationEffect}. Such a valid vibration serialization
+ * should be parse-able by {@link VibrationXmlParser}.
+ *
+ * The example below represents a valid customization for effect IDs 10 and 11.
+ *
+ * <pre>
+ *   {@code
+ *     <haptic-feedback-constants>
+ *          <constant id="10">
+ *              // Valid Vibration Serialization
+ *          </constant>
+ *          <constant id="11">
+ *              // Valid Vibration Serialization
+ *          </constant>
+ *     </haptic-feedback-constants>
+ *   }
+ * </pre>
+ *
+ * <p>After a successful parsing of the customization XML file, it returns a {@link SparseArray}
+ * that maps each customized haptic feedback effect ID to its respective {@link VibrationEffect}.
+ *
+ * @hide
+ */
+final class HapticFeedbackCustomization {
+    private static final String TAG = "HapticFeedbackCustomization";
+
+    /** The outer-most tag for haptic feedback customizations.  */
+    private static final String TAG_CONSTANTS = "haptic-feedback-constants";
+    /** The tag defining a customization for a single haptic feedback constant. */
+    private static final String TAG_CONSTANT = "constant";
+
+    /**
+     * Attribute for {@link TAG_CONSTANT}, specifying the haptic feedback constant to
+     * customize.
+     */
+    private static final String ATTRIBUTE_ID = "id";
+
+    /**
+     * Parses the haptic feedback vibration customization XML file for the device, and provides a
+     * mapping of the customized effect IDs to their respective {@link VibrationEffect}s.
+     *
+     * <p>This is potentially expensive, so avoid calling repeatedly. One call is enough, and the
+     * caller should process the returned mapping (if any) for further queries.
+     *
+     * @param res {@link Resources} object to be used for reading the device's resources.
+     * @return a {@link SparseArray} that maps each customized haptic feedback effect ID to its
+     *      respective {@link VibrationEffect}, or {@code null}, if the device has not configured
+     *      a file for haptic feedback constants customization.
+     * @throws {@link IOException} if an IO error occurs while parsing the customization XML.
+     * @throws {@link CustomizationParserException} for any non-IO error that occurs when parsing
+     *      the XML, like an invalid XML content or an invalid haptic feedback constant.
+     *
+     * @hide
+     */
+    @Nullable
+    static SparseArray<VibrationEffect> loadVibrations(Resources res)
+            throws CustomizationParserException, IOException {
+        try {
+            return loadVibrationsInternal(res);
+        } catch (VibrationXmlParser.VibrationXmlParserException
+                | XmlParserException
+                | XmlPullParserException e) {
+            throw new CustomizationParserException(
+                    "Error parsing haptic feedback customization file.", e);
+        }
+    }
+
+    @Nullable
+    private static SparseArray<VibrationEffect> loadVibrationsInternal(Resources res) throws
+            CustomizationParserException,
+            IOException,
+            VibrationXmlParser.VibrationXmlParserException,
+            XmlParserException,
+            XmlPullParserException {
+        String customizationFile =
+                res.getString(
+                        com.android.internal.R.string.config_hapticFeedbackCustomizationFile);
+        if (TextUtils.isEmpty(customizationFile)) {
+            Slog.d(TAG, "Customization file not configured.");
+            return null;
+        }
+
+        FileReader fileReader;
+        try {
+            fileReader = new FileReader(customizationFile);
+        } catch (FileNotFoundException e) {
+            Slog.d(TAG, "Specified customization file not found.");
+            return  null;
+        }
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+        parser.setInput(fileReader);
+
+        XmlReader.readDocumentStartTag(parser, TAG_CONSTANTS);
+        XmlValidator.checkTagHasNoUnexpectedAttributes(parser);
+        int rootDepth = parser.getDepth();
+
+        SparseArray<VibrationEffect> mapping = new SparseArray<>();
+        while (XmlReader.readNextTagWithin(parser, rootDepth)) {
+            XmlValidator.checkStartTag(parser, TAG_CONSTANT);
+            int customizationDepth = parser.getDepth();
+
+            // Only attribute in tag is the `id` attribute.
+            XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_ID);
+            int effectId = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_ID);
+            if (mapping.contains(effectId)) {
+                throw new CustomizationParserException(
+                        "Multiple customizations found for effect " + effectId);
+            }
+
+            // Move the parser one step into the `<constant>` tag.
+            XmlValidator.checkParserCondition(
+                    XmlReader.readNextTagWithin(parser, customizationDepth),
+                    "Unsupported empty customization tag");
+
+            VibrationEffect effect = VibrationXmlParser.parseTag(
+                    parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
+            if (effect.getDuration() == Long.MAX_VALUE) {
+                throw new CustomizationParserException(String.format(
+                        "Vibration for effect ID %d is repeating, which is not allowed as a"
+                        + " haptic feedback: %s", effectId, effect));
+            }
+            mapping.put(effectId, effect);
+
+            XmlReader.readEndTag(parser, TAG_CONSTANT, customizationDepth);
+        }
+
+        // Make checks that the XML ends well.
+        XmlReader.readEndTag(parser, TAG_CONSTANTS, rootDepth);
+        XmlReader.readDocumentEndTag(parser);
+
+        return mapping;
+    }
+
+    /**
+     * Represents an error while parsing a haptic feedback customization XML.
+     *
+     * @hide
+     */
+    static final class CustomizationParserException extends Exception {
+        private CustomizationParserException(String message) {
+            super(message);
+        }
+
+        private CustomizationParserException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
new file mode 100644
index 0000000..308ce4f
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.view.HapticFeedbackConstants;
+
+import java.io.PrintWriter;
+
+/**
+ * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
+ *
+ * @hide
+ */
+public final class HapticFeedbackVibrationProvider {
+    private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+    private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
+    private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+            VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
+    private final Vibrator mVibrator;
+    private final boolean mHapticTextHandleEnabled;
+    // Vibrator effect for haptic feedback during boot when safe mode is enabled.
+    private final VibrationEffect mSafeModeEnabledVibrationEffect;
+
+    /** @hide */
+    public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
+        mVibrator = vibrator;
+        mHapticTextHandleEnabled = res.getBoolean(
+                com.android.internal.R.bool.config_enableHapticTextHandle);
+        mSafeModeEnabledVibrationEffect =
+                VibrationSettings.createEffectFromResource(
+                        res, com.android.internal.R.array.config_safeModeEnabledVibePattern);
+    }
+
+    /**
+     * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in
+     * {@link HapticFeedbackConstants}).
+     *
+     * @param effectId the haptic feedback effect ID whose respective vibration we want to get.
+     * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
+     *          the provided effect ID is not supported.
+     */
+    @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) {
+        switch (effectId) {
+            case HapticFeedbackConstants.CONTEXT_CLICK:
+            case HapticFeedbackConstants.GESTURE_END:
+            case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
+            case HapticFeedbackConstants.SCROLL_TICK:
+            case HapticFeedbackConstants.SEGMENT_TICK:
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+
+            case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+                if (!mHapticTextHandleEnabled) {
+                    return null;
+                }
+                // fallthrough
+            case HapticFeedbackConstants.CLOCK_TICK:
+            case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
+                return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+
+            case HapticFeedbackConstants.KEYBOARD_RELEASE:
+            case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
+            case HapticFeedbackConstants.ENTRY_BUMP:
+            case HapticFeedbackConstants.DRAG_CROSSING:
+                return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
+
+            case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+            case HapticFeedbackConstants.VIRTUAL_KEY:
+            case HapticFeedbackConstants.EDGE_RELEASE:
+            case HapticFeedbackConstants.CALENDAR_DATE:
+            case HapticFeedbackConstants.CONFIRM:
+            case HapticFeedbackConstants.GESTURE_START:
+            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+            case HapticFeedbackConstants.SCROLL_LIMIT:
+                return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+            case HapticFeedbackConstants.LONG_PRESS:
+            case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+            case HapticFeedbackConstants.DRAG_START:
+            case HapticFeedbackConstants.EDGE_SQUEEZE:
+                return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+            case HapticFeedbackConstants.REJECT:
+                return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+
+            case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+                return mSafeModeEnabledVibrationEffect;
+
+            case HapticFeedbackConstants.ASSISTANT_BUTTON:
+                if (mVibrator.areAllPrimitivesSupported(
+                        VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+                        VibrationEffect.Composition.PRIMITIVE_TICK)) {
+                    // quiet ramp, short pause, then sharp tick
+                    return VibrationEffect.startComposition()
+                            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+                            .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+                            .compose();
+                }
+                // fallback for devices without composition support
+                return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+            case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
+                return getScaledPrimitiveOrElseEffect(
+                        VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
+                        VibrationEffect.EFFECT_TEXTURE_TICK);
+
+            case HapticFeedbackConstants.TOGGLE_ON:
+                return getScaledPrimitiveOrElseEffect(
+                        VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
+                        VibrationEffect.EFFECT_TICK);
+
+            case HapticFeedbackConstants.TOGGLE_OFF:
+                return getScaledPrimitiveOrElseEffect(
+                        VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
+                        VibrationEffect.EFFECT_TEXTURE_TICK);
+
+            case HapticFeedbackConstants.NO_HAPTICS:
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Provides the {@link VibrationAttributes} that should be used for a haptic feedback.
+     *
+     * @param effectId the haptic feedback effect ID whose respective vibration attributes we want
+     *      to get.
+     * @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
+     *      vibration intensity settings. {@code false} otherwise.
+     * @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
+     */
+    public VibrationAttributes getVibrationAttributesForHapticFeedback(
+            int effectId, boolean bypassVibrationIntensitySetting) {
+        VibrationAttributes attrs;
+        switch (effectId) {
+            case HapticFeedbackConstants.EDGE_SQUEEZE:
+            case HapticFeedbackConstants.EDGE_RELEASE:
+                attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
+                break;
+            case HapticFeedbackConstants.ASSISTANT_BUTTON:
+            case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+            case HapticFeedbackConstants.SCROLL_TICK:
+            case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+            case HapticFeedbackConstants.SCROLL_LIMIT:
+                attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+                break;
+            default:
+                attrs = TOUCH_VIBRATION_ATTRIBUTES;
+        }
+        if (bypassVibrationIntensitySetting) {
+            attrs = new VibrationAttributes.Builder(attrs)
+                    .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+                    .build();
+        }
+        return attrs;
+    }
+
+    /** Dumps relevant state. */
+    public void dump(String prefix, PrintWriter pw) {
+        pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+    }
+
+    private VibrationEffect getScaledPrimitiveOrElseEffect(
+            int primitiveId, float scale, int elseEffectId) {
+        if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
+            return VibrationEffect.startComposition()
+                    .addPrimitive(primitiveId, scale)
+                    .compose();
+        } else {
+            return VibrationEffect.get(elseEffectId);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/vibrator/TEST_MAPPING b/services/core/java/com/android/server/vibrator/TEST_MAPPING
new file mode 100644
index 0000000..f0a7e47
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksVibratorServicesTests",
+      "options": [
+        {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksVibratorServicesTests",
+      "options": [
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 4ae7c77..dbd6bf4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -698,7 +698,23 @@
 
     @Nullable
     private VibrationEffect createEffectFromResource(int resId) {
-        long[] timings = getLongIntArray(mContext.getResources(), resId);
+        return createEffectFromResource(mContext.getResources(), resId);
+    }
+
+    /**
+     * Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource..
+     *
+     * <p>If the timings array is {@code null} or empty, it returns {@code null}.
+     *
+     * <p>If the timings array has a size of one, it returns a one-shot vibration with duration that
+     * is equal to the single value in the array.
+     *
+     * <p>If the timings array has more than one values, it returns a non-repeating wave-form
+     * vibration with off-on timings as per the provided timings array.
+     */
+    @Nullable
+    static VibrationEffect createEffectFromResource(Resources res, int resId) {
+        long[] timings = getLongIntArray(res, resId);
         return createEffectFromTimings(timings);
     }
 
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7996434..b55af76 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1016,8 +1016,7 @@
                         && mWallpaper.userId == mCurrentUserId) {
                     Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
                             + ", reverting to built-in wallpaper!");
-                    clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
-                            null);
+                    clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
                 }
             }
         };
@@ -1197,7 +1196,7 @@
                 } else {
                     // Timeout
                     Slog.w(TAG, "Reverting to built-in wallpaper!");
-                    clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+                    clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
                     final String flattened = wpService.flattenToString();
                     EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
                             flattened.substring(0, Math.min(flattened.length(),
@@ -1236,8 +1235,7 @@
                             } else {
                                 if (mLmkLimitRebindRetries <= 0) {
                                     Slog.w(TAG, "Reverting to built-in wallpaper due to lmk!");
-                                    clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
-                                            null);
+                                    clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
                                     mLmkLimitRebindRetries = LMK_RECONNECT_REBIND_RETRIES;
                                     return;
                                 }
@@ -1256,7 +1254,7 @@
                                     && mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
                                     > SystemClock.uptimeMillis()) {
                                 Slog.w(TAG, "Reverting to built-in wallpaper!");
-                                clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+                                clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
                             } else {
                                 mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
                                 tryToRebind();
@@ -1497,7 +1495,7 @@
                                 wallpaper, null)) {
                             Slog.w(TAG, "Wallpaper " + wpService
                                     + " no longer available; reverting to default");
-                            clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+                            clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
                         }
                     }
                 }
@@ -1585,7 +1583,7 @@
                     if (doit) {
                         Slog.w(TAG, "Wallpaper uninstalled, removing: "
                                 + wallpaper.wallpaperComponent);
-                        clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+                        clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
                     }
                 }
             }
@@ -1606,7 +1604,7 @@
                 } catch (NameNotFoundException e) {
                     Slog.w(TAG, "Wallpaper component gone, removing: "
                             + wallpaper.wallpaperComponent);
-                    clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+                    clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
                 }
             }
             if (wallpaper.nextWallpaperComponent != null
@@ -1638,7 +1636,8 @@
         mShuttingDown = false;
         mImageWallpaper = ComponentName.unflattenFromString(
                 context.getResources().getString(R.string.image_wallpaper_component));
-        mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+        ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+        mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mIPackageManager = AppGlobals.getPackageManager();
@@ -1721,7 +1720,7 @@
                 if (DEBUG) {
                     Slog.i(TAG, "Unable to regenerate crop; resetting");
                 }
-                clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
+                clearWallpaperLocked(FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
             }
         } else {
             if (DEBUG) {
@@ -1978,7 +1977,7 @@
                 }
 
                 if (si == null) {
-                    clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
+                    clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, reply);
                 } else {
                     Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
                     // We might end up persisting the current wallpaper data
@@ -2002,10 +2001,10 @@
 
         if (serviceInfo == null) {
             if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
-                clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
-                clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply);
+                clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
+                clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply);
             } else {
-                clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply);
+                clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
             }
             return;
         }
@@ -2038,9 +2037,9 @@
         WallpaperData data = null;
         synchronized (mLock) {
             if (mIsLockscreenLiveWallpaperEnabled) {
-                clearWallpaperLocked(callingPackage, false, which, userId);
+                clearWallpaperLocked(callingPackage, which, userId);
             } else {
-                clearWallpaperLocked(false, which, userId, null);
+                clearWallpaperLocked(which, userId, null);
             }
 
             if (which == FLAG_LOCK) {
@@ -2058,8 +2057,7 @@
         }
     }
 
-    private void clearWallpaperLocked(String callingPackage, boolean defaultFailed,
-            int which, int userId) {
+    private void clearWallpaperLocked(String callingPackage, int which, int userId) {
 
         // Might need to bring it in the first time to establish our rewrite
         if (!mWallpaperMap.contains(userId)) {
@@ -2094,7 +2092,7 @@
                 component = wallpaper.wallpaperComponent;
                 finalWhich = FLAG_LOCK | FLAG_SYSTEM;
             } else {
-                component = defaultFailed ? mImageWallpaper : null;
+                component = null;
                 finalWhich = which;
             }
 
@@ -2114,8 +2112,7 @@
     }
 
     // TODO(b/266818039) remove this version of the method
-    private void clearWallpaperLocked(boolean defaultFailed, int which, int userId,
-            IRemoteCallback reply) {
+    private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
             throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
         }
@@ -2168,9 +2165,7 @@
                 wallpaper.primaryColors = null;
                 wallpaper.imageWallpaperPending = false;
                 if (userId != mCurrentUserId) return;
-                if (bindWallpaperComponentLocked(defaultFailed
-                        ? mImageWallpaper
-                                : null, true, false, wallpaper, reply)) {
+                if (bindWallpaperComponentLocked(null, true, false, wallpaper, reply)) {
                     return;
                 }
             } catch (IllegalArgumentException e1) {
@@ -3523,11 +3518,6 @@
         try {
             if (componentName == null) {
                 componentName = mDefaultWallpaperComponent;
-                if (componentName == null) {
-                    // Fall back to static image wallpaper
-                    componentName = mImageWallpaper;
-                    if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
-                }
             }
             int serviceUserId = wallpaper.userId;
             ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
@@ -3997,7 +3987,8 @@
             mFallbackWallpaper = new WallpaperData(systemUserId, FLAG_SYSTEM);
             mFallbackWallpaper.allowBackup = false;
             mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
-            bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+            bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+                    mFallbackWallpaper, null);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index c5f63ce..a6d5c19 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -285,9 +285,9 @@
         final LaunchingState mLaunchingState;
 
         /** The type can be cold (new process), warm (new activity), or hot (bring to front). */
-        final int mTransitionType;
+        int mTransitionType;
         /** Whether the process was already running when the transition started. */
-        final boolean mProcessRunning;
+        boolean mProcessRunning;
         /** whether the process of the launching activity didn't have any active activity. */
         final boolean mProcessSwitch;
         /** The process state of the launching activity prior to the launch */
@@ -972,6 +972,19 @@
             // App isn't attached to record yet, so match with info.
             if (info.mLastLaunchedActivity.info.applicationInfo == appInfo) {
                 info.mBindApplicationDelayMs = info.calculateCurrentDelay();
+                if (info.mProcessRunning) {
+                    // It was HOT/WARM launch, but the process was died somehow right after the
+                    // launch request.
+                    info.mProcessRunning = false;
+                    info.mTransitionType = TYPE_TRANSITION_COLD_LAUNCH;
+                    final String msg = "Process " + info.mLastLaunchedActivity.info.processName
+                            + " restarted";
+                    Slog.i(TAG, msg);
+                    if (info.mLaunchingState.mTraceName != null) {
+                        Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, msg + "#"
+                                + LaunchingState.sTraceSeqId);
+                    }
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e30673c..ea06b42 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7991,6 +7991,9 @@
                 mLastReportedConfiguration.getMergedConfiguration())) {
             ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */,
                     false /* ignoreVisibility */, true /* isRequestedOrientationChanged */);
+            if (mTransitionController.inPlayingTransition(this)) {
+                mTransitionController.mValidateActivityCompat.add(this);
+            }
         }
 
         mAtmService.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
@@ -9410,6 +9413,9 @@
         if (info.applicationInfo == null) {
             return info.getMinAspectRatio();
         }
+        if (mLetterboxUiController.shouldApplyUserMinAspectRatioOverride()) {
+            return mLetterboxUiController.getUserMinAspectRatio();
+        }
         if (!mLetterboxUiController.shouldOverrideMinAspectRatio()) {
             return info.getMinAspectRatio();
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 50948e1..5553600 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2201,7 +2201,8 @@
             displaySwapping |= s.isDisplaySleepingAndSwapping();
             ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b "
                     + "finishing=%s", s, s.nowVisible, animating, s.finishing);
-            if ((!animating && !displaySwapping) || mService.mShuttingDown) {
+            if ((!animating && !displaySwapping) || mService.mShuttingDown
+                    || s.getRootTask().isForceHiddenForPinnedTask()) {
                 if (!processPausingActivities && s.isState(PAUSING)) {
                     // Defer processing pausing activities in this iteration and reschedule
                     // a delayed idle to reprocess it again
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e261916..d675753 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2750,6 +2750,10 @@
     void onAppTransitionDone() {
         super.onAppTransitionDone();
         mWmService.mWindowsChanged = true;
+        onTransitionFinished();
+    }
+
+    void onTransitionFinished() {
         // If the transition finished callback cannot match the token for some reason, make sure the
         // rotated state is cleared if it is already invisible.
         if (mFixedRotationLaunchingApp != null && !mFixedRotationLaunchingApp.isVisibleRequested()
@@ -6404,9 +6408,9 @@
             // Don't do recursive work.
             return;
         }
-        mInEnsureActivitiesVisible = true;
         mAtmService.mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
+            mInEnsureActivitiesVisible = true;
             forAllRootTasks(rootTask -> {
                 rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                         notifyClients);
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 56edde0..271d71e 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -24,6 +24,7 @@
 
 import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
@@ -40,7 +41,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -48,7 +48,6 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.Gravity;
-import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -56,7 +55,6 @@
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type;
 import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.Button;
@@ -95,6 +93,10 @@
      */
     @Nullable
     private Context mWindowContext;
+    /**
+     * The root display area feature id that the {@link #mWindowContext} is attaching to.
+     */
+    private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED;
     // Local copy of vr mode enabled state, to avoid calling into VrManager with
     // the lock held.
     private boolean mVrModeEnabled;
@@ -206,12 +208,15 @@
     private void handleHide() {
         if (mClingWindow != null) {
             if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
-            // We don't care which root display area the window manager is specifying for removal.
-            try {
-                getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
-            } catch (WindowManager.InvalidDisplayException e) {
-                Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e);
-                return;
+            if (mWindowManager != null) {
+                try {
+                    mWindowManager.removeView(mClingWindow);
+                } catch (WindowManager.InvalidDisplayException e) {
+                    Slog.w(TAG, "Fail to hide the immersive confirmation window because of "
+                            + e);
+                }
+                mWindowManager = null;
+                mWindowContext = null;
             }
             mClingWindow = null;
         }
@@ -394,26 +399,18 @@
      * @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
      *         confirmation window.
      */
-    private WindowManager getWindowManager(int rootDisplayAreaId) {
-        if (mWindowManager == null || mWindowContext == null) {
-            // Create window context to specify the RootDisplayArea
-            final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
-            mWindowContext = mContext.createWindowContext(
-                    IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
-            mWindowManager = mWindowContext.getSystemService(WindowManager.class);
-            return mWindowManager;
+    @NonNull
+    private WindowManager createWindowManager(int rootDisplayAreaId) {
+        if (mWindowManager != null) {
+            throw new IllegalStateException(
+                    "Must not create a new WindowManager while there is an existing one");
         }
-
-        // Update the window context and window manager to specify the RootDisplayArea
+        // Create window context to specify the RootDisplayArea
         final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
-        final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
-        try {
-            wms.attachWindowContextToDisplayArea(mWindowContext.getWindowContextToken(),
-                    IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options);
-        }  catch (RemoteException e) {
-            throw e.rethrowAsRuntimeException();
-        }
-
+        mWindowContextRootDisplayAreaId = rootDisplayAreaId;
+        mWindowContext = mContext.createWindowContext(
+                IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
+        mWindowManager = mWindowContext.getSystemService(WindowManager.class);
         return mWindowManager;
     }
 
@@ -434,14 +431,23 @@
     }
 
     private void handleShow(int rootDisplayAreaId) {
+        if (mClingWindow != null) {
+            if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) {
+                if (DEBUG) Slog.d(TAG, "Immersive mode confirmation has already been shown");
+                return;
+            } else {
+                // Hide the existing confirmation before show a new one in the new root.
+                if (DEBUG) Slog.d(TAG, "Immersive mode confirmation was shown in a different root");
+                handleHide();
+            }
+        }
+
         if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation");
-
         mClingWindow = new ClingWindowView(mContext, mConfirm);
-
         // show the confirmation
-        WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+        final WindowManager.LayoutParams lp = getClingWindowLayoutParams();
         try {
-            getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+            createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
         } catch (WindowManager.InvalidDisplayException e) {
             Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e);
         }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 2b8312c..5f3d517 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -81,6 +81,7 @@
     private boolean mIsLeashReadyForDispatching;
     private final Rect mSourceFrame = new Rect();
     private final Rect mLastSourceFrame = new Rect();
+    private final Rect mLastContainerBounds = new Rect();
     private @NonNull Insets mInsetsHint = Insets.NONE;
     private @Flags int mFlagsFromFrameProvider;
     private @Flags int mFlagsFromServer;
@@ -278,11 +279,31 @@
         // visible. (i.e. No surface, pending insets that were given during layout, etc..)
         if (mServerVisible) {
             mSource.setFrame(mSourceFrame);
+            updateInsetsHint();
         } else {
             mSource.setFrame(0, 0, 0, 0);
         }
     }
 
+    // To be called when mSourceFrame or the window container bounds is changed.
+    private void updateInsetsHint() {
+        if (!mControllable || !mServerVisible) {
+            return;
+        }
+        final Rect bounds = mWindowContainer.getBounds();
+        if (mSourceFrame.equals(mLastSourceFrame) && bounds.equals(mLastContainerBounds)) {
+            return;
+        }
+        mLastSourceFrame.set(mSourceFrame);
+        mLastContainerBounds.set(bounds);
+        mInsetsHint = mSource.calculateInsets(bounds, true /* ignoreVisibility */);
+    }
+
+    @VisibleForTesting
+    Insets getInsetsHint() {
+        return mInsetsHint;
+    }
+
     /** @return A new source computed by the specified window frame in the given display frames. */
     InsetsSource createSimulatedSource(DisplayFrames displayFrames, Rect frame) {
         final InsetsSource source = new InsetsSource(mSource);
@@ -338,15 +359,9 @@
                     mSetLeashPositionConsumer.accept(t);
                 }
             }
-            if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) {
-                final Insets insetsHint = mSource.calculateInsets(
-                        mWindowContainer.getBounds(), true /* ignoreVisibility */);
-                if (!insetsHint.equals(mControl.getInsetsHint())) {
-                    changed = true;
-                    mControl.setInsetsHint(insetsHint);
-                    mInsetsHint = insetsHint;
-                }
-                mLastSourceFrame.set(mSource.getFrame());
+            if (!mControl.getInsetsHint().equals(mInsetsHint)) {
+                mControl.setInsetsHint(mInsetsHint);
+                changed = true;
             }
             if (changed) {
                 mStateController.notifyControlChanged(mControlTarget);
@@ -587,6 +602,11 @@
             pw.print(prefix + "mControl=");
             mControl.dump("", pw);
         }
+        if (mControllable) {
+            pw.print(prefix + "mInsetsHint=");
+            pw.print(mInsetsHint);
+            pw.println();
+        }
         pw.print(prefix);
         pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
         pw.println();
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7a201a7..e945bc1 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -294,16 +294,15 @@
     @NonNull private final SynchedDeviceConfig mDeviceConfig;
 
     LetterboxConfiguration(@NonNull final Context systemUiContext) {
-        this(systemUiContext,
-                new LetterboxConfigurationPersister(systemUiContext,
-                        () -> readLetterboxHorizontalReachabilityPositionFromConfig(
-                                systemUiContext, /* forBookMode */ false),
-                        () -> readLetterboxVerticalReachabilityPositionFromConfig(
-                                systemUiContext, /* forTabletopMode */ false),
-                        () -> readLetterboxHorizontalReachabilityPositionFromConfig(
-                                systemUiContext, /* forBookMode */ true),
-                        () -> readLetterboxVerticalReachabilityPositionFromConfig(
-                                systemUiContext, /* forTabletopMode */ true)));
+        this(systemUiContext, new LetterboxConfigurationPersister(
+                () -> readLetterboxHorizontalReachabilityPositionFromConfig(
+                        systemUiContext, /* forBookMode */ false),
+                () -> readLetterboxVerticalReachabilityPositionFromConfig(
+                        systemUiContext, /* forTabletopMode */ false),
+                () -> readLetterboxHorizontalReachabilityPositionFromConfig(
+                        systemUiContext, /* forBookMode */ true),
+                () -> readLetterboxVerticalReachabilityPositionFromConfig(
+                        systemUiContext, /* forTabletopMode */ true)));
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
index 7563397..38aa903 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationPersister.java
@@ -23,7 +23,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.Context;
 import android.os.Environment;
 import android.os.StrictMode;
 import android.os.StrictMode.ThreadPolicy;
@@ -53,10 +52,8 @@
     private static final String TAG =
             TAG_WITH_CLASS_NAME ? "LetterboxConfigurationPersister" : TAG_WM;
 
-    @VisibleForTesting
-    static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
+    private static final String LETTERBOX_CONFIGURATION_FILENAME = "letterbox_config";
 
-    private final Context mContext;
     private final Supplier<Integer> mDefaultHorizontalReachabilitySupplier;
     private final Supplier<Integer> mDefaultVerticalReachabilitySupplier;
     private final Supplier<Integer> mDefaultBookModeReachabilitySupplier;
@@ -97,36 +94,32 @@
     @NonNull
     private final PersisterQueue mPersisterQueue;
 
-    LetterboxConfigurationPersister(Context systemUiContext,
-            Supplier<Integer> defaultHorizontalReachabilitySupplier,
-            Supplier<Integer> defaultVerticalReachabilitySupplier,
-            Supplier<Integer> defaultBookModeReachabilitySupplier,
-            Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
-        this(systemUiContext, defaultHorizontalReachabilitySupplier,
-                defaultVerticalReachabilitySupplier,
-                defaultBookModeReachabilitySupplier,
-                defaultTabletopModeReachabilitySupplier,
+    LetterboxConfigurationPersister(
+            @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
+            @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
+            @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
+            @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier) {
+        this(defaultHorizontalReachabilitySupplier, defaultVerticalReachabilitySupplier,
+                defaultBookModeReachabilitySupplier, defaultTabletopModeReachabilitySupplier,
                 Environment.getDataSystemDirectory(), new PersisterQueue(),
-                /* completionCallback */ null);
+                /* completionCallback */ null, LETTERBOX_CONFIGURATION_FILENAME);
     }
 
     @VisibleForTesting
-    LetterboxConfigurationPersister(Context systemUiContext,
-            Supplier<Integer> defaultHorizontalReachabilitySupplier,
-            Supplier<Integer> defaultVerticalReachabilitySupplier,
-            Supplier<Integer> defaultBookModeReachabilitySupplier,
-            Supplier<Integer> defaultTabletopModeReachabilitySupplier,
-            File configFolder,
-            PersisterQueue persisterQueue, @Nullable Consumer<String> completionCallback) {
-        mContext = systemUiContext.createDeviceProtectedStorageContext();
+    LetterboxConfigurationPersister(
+            @NonNull Supplier<Integer> defaultHorizontalReachabilitySupplier,
+            @NonNull Supplier<Integer> defaultVerticalReachabilitySupplier,
+            @NonNull Supplier<Integer> defaultBookModeReachabilitySupplier,
+            @NonNull Supplier<Integer> defaultTabletopModeReachabilitySupplier,
+            @NonNull File configFolder, @NonNull PersisterQueue persisterQueue,
+            @Nullable Consumer<String> completionCallback,
+            @NonNull String letterboxConfigurationFileName) {
         mDefaultHorizontalReachabilitySupplier = defaultHorizontalReachabilitySupplier;
         mDefaultVerticalReachabilitySupplier = defaultVerticalReachabilitySupplier;
-        mDefaultBookModeReachabilitySupplier =
-                defaultBookModeReachabilitySupplier;
-        mDefaultTabletopModeReachabilitySupplier =
-                defaultTabletopModeReachabilitySupplier;
+        mDefaultBookModeReachabilitySupplier = defaultBookModeReachabilitySupplier;
+        mDefaultTabletopModeReachabilitySupplier = defaultTabletopModeReachabilitySupplier;
         mCompletionCallback = completionCallback;
-        final File prefFiles = new File(configFolder, LETTERBOX_CONFIGURATION_FILENAME);
+        final File prefFiles = new File(configFolder, letterboxConfigurationFileName);
         mConfigurationFile = new AtomicFile(prefFiles);
         mPersisterQueue = persisterQueue;
         runWithDiskReadsThreadPolicy(this::readCurrentConfiguration);
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 39f7570..394105a 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -40,6 +40,12 @@
 import static android.content.pm.ActivityInfo.isFixedOrientation;
 import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
 import static android.content.pm.ActivityInfo.screenOrientationToString;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
@@ -103,6 +109,7 @@
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.RemoteException;
 import android.util.Slog;
 import android.view.InsetsSource;
 import android.view.InsetsState;
@@ -237,6 +244,10 @@
     // Counter for ActivityRecord#setRequestedOrientation
     private int mSetOrientationRequestCounter = 0;
 
+    // The min aspect ratio override set by user
+    @PackageManager.UserMinAspectRatio
+    private int mUserAspectRatio = USER_MIN_ASPECT_RATIO_UNSET;
+
     // The CompatDisplayInsets of the opaque activity beneath the translucent one.
     private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets;
 
@@ -1059,7 +1070,7 @@
 
     private float getDefaultMinAspectRatioForUnresizableApps() {
         if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()
-                || mActivityRecord.getDisplayContent() == null) {
+                || mActivityRecord.getDisplayArea() == null) {
             return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
                     > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
                             ? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
@@ -1071,8 +1082,8 @@
 
     float getSplitScreenAspectRatio() {
         // Getting the same aspect ratio that apps get in split screen.
-        final DisplayContent displayContent = mActivityRecord.getDisplayContent();
-        if (displayContent == null) {
+        final DisplayArea displayArea = mActivityRecord.getDisplayArea();
+        if (displayArea == null) {
             return getDefaultMinAspectRatioForUnresizableApps();
         }
         int dividerWindowWidth =
@@ -1080,7 +1091,7 @@
         int dividerInsets =
                 getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
         int dividerSize = dividerWindowWidth - dividerInsets * 2;
-        final Rect bounds = new Rect(displayContent.getWindowConfiguration().getAppBounds());
+        final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
         if (bounds.width() >= bounds.height()) {
             bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
             bounds.right = bounds.centerX();
@@ -1091,14 +1102,57 @@
         return computeAspectRatio(bounds);
     }
 
+    boolean shouldApplyUserMinAspectRatioOverride() {
+        if (!mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()) {
+            return false;
+        }
+
+        try {
+            final int userAspectRatio = mActivityRecord.mAtmService.getPackageManager()
+                    .getUserMinAspectRatio(mActivityRecord.packageName, mActivityRecord.mUserId);
+            mUserAspectRatio = userAspectRatio;
+            return userAspectRatio != USER_MIN_ASPECT_RATIO_UNSET;
+        } catch (RemoteException e) {
+            // Don't apply user aspect ratio override
+            Slog.w(TAG, "Exception thrown retrieving aspect ratio user override " + this, e);
+            return false;
+        }
+    }
+
+    float getUserMinAspectRatio() {
+        switch (mUserAspectRatio) {
+            case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE:
+                return getDisplaySizeMinAspectRatio();
+            case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN:
+                return getSplitScreenAspectRatio();
+            case USER_MIN_ASPECT_RATIO_16_9:
+                return 16 / 9f;
+            case USER_MIN_ASPECT_RATIO_4_3:
+                return 4 / 3f;
+            case USER_MIN_ASPECT_RATIO_3_2:
+                return 3 / 2f;
+            default:
+                throw new AssertionError("Unexpected user min aspect ratio override: "
+                        + mUserAspectRatio);
+        }
+    }
+
+    private float getDisplaySizeMinAspectRatio() {
+        final DisplayArea displayArea = mActivityRecord.getDisplayArea();
+        if (displayArea == null) {
+            return mActivityRecord.info.getMinAspectRatio();
+        }
+        final Rect bounds = new Rect(displayArea.getWindowConfiguration().getAppBounds());
+        return computeAspectRatio(bounds);
+    }
+
     private float getDefaultMinAspectRatio() {
-        final DisplayContent displayContent = mActivityRecord.getDisplayContent();
-        if (displayContent == null
+        if (mActivityRecord.getDisplayArea() == null
                 || !mLetterboxConfiguration
                     .getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()) {
             return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
         }
-        return computeAspectRatio(new Rect(displayContent.getBounds()));
+        return getDisplaySizeMinAspectRatio();
     }
 
     Resources getResources() {
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index b71d918b..f33ecaa 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -684,6 +684,26 @@
         }
     }
 
+    /**
+     * Removes the oldest recent task that is compatible with the given one. This is possible if
+     * the task windowing mode changed after being added to the Recents.
+     */
+    void removeCompatibleRecentTask(Task task) {
+        final int taskIndex = mTasks.indexOf(task);
+        if (taskIndex < 0) {
+            return;
+        }
+
+        final int candidateIndex = findRemoveIndexForTask(task, false /* includingSelf */);
+        if (candidateIndex == -1) {
+            // Nothing to trim
+            return;
+        }
+
+        final Task taskToRemove = taskIndex > candidateIndex ? task : mTasks.get(candidateIndex);
+        remove(taskToRemove);
+    }
+
     void removeTasksByPackageName(String packageName, int userId) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final Task task = mTasks.get(i);
@@ -1546,6 +1566,10 @@
      * list (if any).
      */
     private int findRemoveIndexForAddTask(Task task) {
+        return findRemoveIndexForTask(task, true /* includingSelf */);
+    }
+
+    private int findRemoveIndexForTask(Task task, boolean includingSelf) {
         final int recentsCount = mTasks.size();
         final Intent intent = task.intent;
         final boolean document = intent != null && intent.isDocument();
@@ -1601,6 +1625,8 @@
                     // existing task
                     continue;
                 }
+            } else if (!includingSelf) {
+                continue;
             }
             return i;
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6e46f3f..2f0c303 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1849,9 +1849,8 @@
             // Don't do recursive work.
             return;
         }
-
+        mTaskSupervisor.beginActivityVisibilityUpdate();
         try {
-            mTaskSupervisor.beginActivityVisibilityUpdate();
             // First the front root tasks. In case any are not fullscreen and are in front of home.
             for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
                 final DisplayContent display = getChildAt(displayNdx);
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index c914fa1..fe3094e 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -293,26 +293,7 @@
             throw new SecurityException(msg);
         }
         // Check if the caller is allowed to launch on the specified display area.
-        final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
-        TaskDisplayArea taskDisplayArea = daToken != null
-                ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
-
-        // If we do not have a task display area token, check if the launch task display area
-        // feature id is specified.
-        if (taskDisplayArea == null) {
-            final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
-            if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
-                final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
-                        ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
-                final DisplayContent dc = supervisor.mRootWindowContainer
-                        .getDisplayContent(launchDisplayId);
-                if (dc != null) {
-                    taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
-                            tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
-                }
-            }
-        }
-
+        final TaskDisplayArea taskDisplayArea = getLaunchTaskDisplayArea(options, supervisor);
         if (aInfo != null && taskDisplayArea != null
                 && !supervisor.isCallerAllowedToLaunchOnTaskDisplayArea(callingPid, callingUid,
                 taskDisplayArea, aInfo)) {
@@ -428,6 +409,32 @@
         }
     }
 
+    @VisibleForTesting
+    TaskDisplayArea getLaunchTaskDisplayArea(ActivityOptions options,
+            ActivityTaskSupervisor supervisor) {
+        final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
+        TaskDisplayArea taskDisplayArea = daToken != null
+                ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
+        if (taskDisplayArea != null) {
+            return taskDisplayArea;
+        }
+
+        // If we do not have a task display area token, check if the launch task display area
+        // feature id is specified.
+        final int launchTaskDisplayAreaFeatureId = options.getLaunchTaskDisplayAreaFeatureId();
+        if (launchTaskDisplayAreaFeatureId != FEATURE_UNDEFINED) {
+            final int launchDisplayId = options.getLaunchDisplayId() == INVALID_DISPLAY
+                    ? DEFAULT_DISPLAY : options.getLaunchDisplayId();
+            final DisplayContent dc = supervisor.mRootWindowContainer
+                    .getDisplayContent(launchDisplayId);
+            if (dc != null) {
+                taskDisplayArea = dc.getItemFromTaskDisplayAreas(tda ->
+                        tda.mFeatureId == launchTaskDisplayAreaFeatureId ? tda : null);
+            }
+        }
+        return taskDisplayArea;
+    }
+
     private boolean isAssistant(ActivityTaskManagerService atmService, int callingUid) {
         if (atmService.mActiveVoiceInteractionServiceComponent == null) {
             return false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 92e90ae..640ae3f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4504,6 +4504,10 @@
         return mForceHiddenFlags != 0;
     }
 
+    boolean isForceHiddenForPinnedTask() {
+        return (mForceHiddenFlags & FLAG_FORCE_HIDDEN_FOR_PINNED_TASK) != 0;
+    }
+
     @Override
     protected boolean isForceTranslucent() {
         return mForceTranslucent;
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 0c1f33c..92c0987 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1020,7 +1020,7 @@
         final WindowContainer<?> parent = getParent();
         final Task thisTask = asTask();
         if (thisTask != null && parent.asTask() == null
-                && mTransitionController.isTransientHide(thisTask)) {
+                && mTransitionController.isTransientVisible(thisTask)) {
             // Keep transient-hide root tasks visible. Non-root tasks still follow standard rule.
             return TASK_FRAGMENT_VISIBILITY_VISIBLE;
         }
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a143540..789e3d2 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -407,6 +407,36 @@
         return false;
     }
 
+    /** Returns {@code true} if the task should keep visible if this is a transient transition. */
+    boolean isTransientVisible(@NonNull Task task) {
+        if (mTransientLaunches == null) return false;
+        int occludedCount = 0;
+        final int numTransient = mTransientLaunches.size();
+        for (int i = numTransient - 1; i >= 0; --i) {
+            final Task transientRoot = mTransientLaunches.keyAt(i).getRootTask();
+            if (transientRoot == null) continue;
+            final WindowContainer<?> rootParent = transientRoot.getParent();
+            if (rootParent == null || rootParent.getTopChild() == transientRoot) continue;
+            final ActivityRecord topOpaque = mController.mAtm.mTaskSupervisor
+                    .mOpaqueActivityHelper.getOpaqueActivity(rootParent);
+            if (transientRoot.compareTo(topOpaque.getRootTask()) < 0) {
+                occludedCount++;
+            }
+        }
+        if (occludedCount == numTransient) {
+            for (int i = mTransientLaunches.size() - 1; i >= 0; --i) {
+                if (mTransientLaunches.keyAt(i).isDescendantOf(task)) {
+                    // Keep transient activity visible until transition finished, so it won't pause
+                    // with transient-hide tasks that may delay resuming the next top.
+                    return true;
+                }
+            }
+            // Let transient-hide activities pause before transition is finished.
+            return false;
+        }
+        return isInTransientHide(task);
+    }
+
     boolean canApplyDim(@NonNull Task task) {
         if (mTransientLaunches == null) return true;
         final Dimmer dimmer = task.getDimmer();
@@ -1298,6 +1328,7 @@
             if (asyncRotationController != null && containsChangeFor(dc, mTargets)) {
                 asyncRotationController.onTransitionFinished();
             }
+            dc.onTransitionFinished();
             if (hasParticipatedDisplay && dc.mDisplayRotationCompatPolicy != null) {
                 final ChangeInfo changeInfo = mChanges.get(dc);
                 if (changeInfo != null
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 881eddc..dfaa174 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -30,6 +30,7 @@
 import android.app.ActivityManager;
 import android.app.IApplicationThread;
 import android.app.WindowConfiguration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
@@ -141,6 +142,14 @@
     final ArrayList<ActivityRecord> mValidateCommitVis = new ArrayList<>();
 
     /**
+     * List of activity-level participants. ActivityRecord is not expected to change independently,
+     * however, recent compatibility logic can now cause this at arbitrary times determined by
+     * client code. If it happens during an animation, the surface can be left at the wrong spot.
+     * TODO(b/290237710) remove when compat logic is moved.
+     */
+    final ArrayList<ActivityRecord> mValidateActivityCompat = new ArrayList<>();
+
+    /**
      * Currently playing transitions (in the order they were started). When finished, records are
      * removed from this list.
      */
@@ -468,15 +477,22 @@
         if (mCollectingTransition != null && mCollectingTransition.isInTransientHide(task)) {
             return true;
         }
-        for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
-            if (mWaitingTransitions.get(i).isInTransientHide(task)) return true;
-        }
         for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
             if (mPlayingTransitions.get(i).isInTransientHide(task)) return true;
         }
         return false;
     }
 
+    boolean isTransientVisible(@NonNull Task task) {
+        if (mCollectingTransition != null && mCollectingTransition.isTransientVisible(task)) {
+            return true;
+        }
+        for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
+            if (mPlayingTransitions.get(i).isTransientVisible(task)) return true;
+        }
+        return false;
+    }
+
     boolean canApplyDim(@Nullable Task task) {
         if (task == null) {
             // Always allow non-activity window.
@@ -896,6 +912,14 @@
             }
         }
         mValidateCommitVis.clear();
+        for (int i = 0; i < mValidateActivityCompat.size(); ++i) {
+            ActivityRecord ar = mValidateActivityCompat.get(i);
+            if (ar.getSurfaceControl() == null) continue;
+            final Point tmpPos = new Point();
+            ar.getRelativePosition(tmpPos);
+            ar.getSyncTransaction().setPosition(ar.getSurfaceControl(), tmpPos.x, tmpPos.y);
+        }
+        mValidateActivityCompat.clear();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0eb452d..164c8b0 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1615,6 +1615,7 @@
         final int count = tasksToReparent.size();
         for (int i = 0; i < count; ++i) {
             final Task task = tasksToReparent.get(i);
+            final int prevWindowingMode = task.getWindowingMode();
             if (syncId >= 0) {
                 addToSyncSet(syncId, task);
             }
@@ -1628,6 +1629,12 @@
                         hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
                         false /*moveParents*/, "processChildrenTaskReparentHierarchyOp");
             }
+            // Trim the compatible Recent task (if any) after the Task is reparented and now has
+            // a different windowing mode, in order to prevent redundant Recent tasks after
+            // reparenting.
+            if (prevWindowingMode != task.getWindowingMode()) {
+                mService.mTaskSupervisor.mRecentTasks.removeCompatibleRecentTask(task);
+            }
         }
 
         if (transition != null) transition.collect(newParent);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
index 24d0521..0265453 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetPolicySerializer.java
@@ -32,9 +32,8 @@
 
 // TODO(scottjonathan): Replace with generic set implementation
 final class StringSetPolicySerializer extends PolicySerializer<Set<String>> {
-    private static final String ATTR_VALUES = ":strings";
+    private static final String ATTR_VALUES = "strings";
     private static final String ATTR_VALUES_SEPARATOR = ";";
-
     @Override
     void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer,
             @NonNull Set<String> value) throws IOException {
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index be7feb5..648b904 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -9,6 +9,9 @@
       ]
     },
     {
+      "name": "CtsPackageManagerIncrementalStatsHostTestCases"
+    },
+    {
       "name": "CtsIncrementalInstallHostTestCases"
     },
     {
diff --git a/services/permission/OWNERS b/services/permission/OWNERS
index 6c6c9fc..e464038 100644
--- a/services/permission/OWNERS
+++ b/services/permission/OWNERS
@@ -1,4 +1,5 @@
-ashfall@google.com
+#Bug component: 137825
+
 joecastro@google.com
 ntmyren@google.com
 zhanghai@google.com
diff --git a/services/tests/InputMethodSystemServerTests/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
index 212ec14..bef56ce 100644
--- a/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/AndroidManifest.xml
@@ -17,7 +17,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.frameworks.inputmethodtests">
 
-    <uses-sdk android:targetSdkVersion="31" />
     <queries>
         <intent>
             <action android:name="android.view.InputMethod" />
diff --git a/services/tests/InputMethodSystemServerTests/TEST_MAPPING b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
index 77e32a7..cedbfd2b 100644
--- a/services/tests/InputMethodSystemServerTests/TEST_MAPPING
+++ b/services/tests/InputMethodSystemServerTests/TEST_MAPPING
@@ -9,5 +9,16 @@
         {"exclude-annotation": "org.junit.Ignore"}
       ]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksImeTests",
+      "options": [
+        {"include-filter": "com.android.inputmethodservice"},
+        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+        {"exclude-annotation": "org.junit.Ignore"}
+      ]
+    }
   ]
 }
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
index 0104f71..b7de749 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/AndroidManifest.xml
@@ -18,8 +18,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.inputmethod.imetests">
 
-    <uses-sdk android:targetSdkVersion="31" />
-
     <!-- Permissions required for granting and logging -->
     <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
index 898658e..e8acb06 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/inputmethodservice/InputMethodServiceTest.java
@@ -20,6 +20,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.fail;
+
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -45,6 +47,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.IOException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -56,9 +59,9 @@
     private static final String EDIT_TEXT_DESC = "Input box";
     private static final long TIMEOUT_IN_SECONDS = 3;
     private static final String ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
-            "settings put secure show_ime_with_hard_keyboard 1";
+            "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 1";
     private static final String DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD =
-            "settings put secure show_ime_with_hard_keyboard 0";
+            "settings put secure " + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD + " 0";
 
     private Instrumentation mInstrumentation;
     private UiDevice mUiDevice;
@@ -82,29 +85,19 @@
         mUiDevice.freezeRotation();
         mUiDevice.setOrientationNatural();
         // Waits for input binding ready.
-        eventually(
-                () -> {
-                    mInputMethodService =
-                            InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting();
-                    assertThat(mInputMethodService).isNotNull();
+        eventually(() -> {
+            mInputMethodService =
+                    InputMethodServiceWrapper.getInputMethodServiceWrapperForTesting();
+            assertThat(mInputMethodService).isNotNull();
 
-                    // The editor won't bring up keyboard by default.
-                    assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
-                    assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse();
-                });
-        // Save the original value of show_ime_with_hard_keyboard in Settings.
+            // The editor won't bring up keyboard by default.
+            assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
+            assertThat(mInputMethodService.getCurrentInputViewStarted()).isFalse();
+        });
+        // Save the original value of show_ime_with_hard_keyboard from Settings.
         mShowImeWithHardKeyboardEnabled = Settings.Secure.getInt(
                 mInputMethodService.getContentResolver(),
                 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
-        // Disable showing Ime with hard keyboard because it is the precondition the for most test
-        // cases
-        if (mShowImeWithHardKeyboardEnabled) {
-            executeShellCommand(DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
-        }
-        mInputMethodService.getResources().getConfiguration().keyboard =
-                Configuration.KEYBOARD_NOKEYS;
-        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
-                Configuration.HARDKEYBOARDHIDDEN_YES;
     }
 
     @After
@@ -112,82 +105,141 @@
         mUiDevice.unfreezeRotation();
         executeShellCommand("ime disable " + mInputMethodId);
         // Change back the original value of show_ime_with_hard_keyboard in Settings.
-        executeShellCommand(mShowImeWithHardKeyboardEnabled ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
+        executeShellCommand(mShowImeWithHardKeyboardEnabled
+                ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
                 : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
     }
 
+    /**
+     * This checks that the IME can be shown and hidden by user actions
+     * (i.e. tapping on an EditText, tapping the Home button).
+     */
     @Test
-    public void testShowHideKeyboard_byUserAction() throws InterruptedException {
+    public void testShowHideKeyboard_byUserAction() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
         // Performs click on editor box to bring up the soft keyboard.
         Log.i(TAG, "Click on EditText.");
-        verifyInputViewStatus(() -> clickOnEditorText(), true /* inputViewStarted */);
-
-        // Press back key to hide soft keyboard.
-        Log.i(TAG, "Press back");
         verifyInputViewStatus(
-                () -> assertThat(mUiDevice.pressHome()).isTrue(), false /* inputViewStarted */);
+                () -> clickOnEditorText(),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+        // Press home key to hide soft keyboard.
+        Log.i(TAG, "Press home");
+        verifyInputViewStatus(
+                () -> assertThat(mUiDevice.pressHome()).isTrue(),
+                true /* expected */,
+                false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
     }
 
+    /**
+     * This checks that the IME can be shown and hidden using the WindowInsetsController APIs.
+     */
     @Test
-    public void testShowHideKeyboard_byApi() throws InterruptedException {
+    public void testShowHideKeyboard_byApi() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
         // Triggers to show IME via public API.
         verifyInputViewStatus(
                 () -> assertThat(mActivity.showImeWithWindowInsetsController()).isTrue(),
+                true /* expected */,
                 true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
 
         // Triggers to hide IME via public API.
         verifyInputViewStatusOnMainSync(
-                () -> assertThat(mActivity.hideImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                () -> assertThat(mActivity.hideImeWithWindowInsetsController()).isTrue(),
+                true /* expected */,
                 false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
     }
 
+    /**
+     * This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
+     */
     @Test
-    public void testShowHideSelf() throws InterruptedException {
-        // IME requests to show itself without any flags: expect shown.
+    public void testShowHideSelf() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
+        // IME request to show itself without any flags, expect shown.
         Log.i(TAG, "Call IMS#requestShowSelf(0)");
         verifyInputViewStatusOnMainSync(
-                () -> mInputMethodService.requestShowSelf(0), true /* inputViewStarted */);
+                () -> mInputMethodService.requestShowSelf(0 /* flags */),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
 
-        // IME requests to hide itself with flag: HIDE_IMPLICIT_ONLY, expect not hide (shown).
+        // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect not hide (shown).
         Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
         verifyInputViewStatusOnMainSync(
                 () -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY),
+                false /* expected */,
                 true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
 
-        // IME request to hide itself without any flags: expect hidden.
+        // IME request to hide itself without any flags, expect hidden.
         Log.i(TAG, "Call IMS#requestHideSelf(0)");
         verifyInputViewStatusOnMainSync(
-                () -> mInputMethodService.requestHideSelf(0), false /* inputViewStarted */);
+                () -> mInputMethodService.requestHideSelf(0 /* flags */),
+                true /* expected */,
+                false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
 
-        // IME request to show itself with flag SHOW_IMPLICIT: expect shown.
+        // IME request to show itself with flag SHOW_IMPLICIT, expect shown.
         Log.i(TAG, "Call IMS#requestShowSelf(InputMethodManager.SHOW_IMPLICIT)");
         verifyInputViewStatusOnMainSync(
                 () -> mInputMethodService.requestShowSelf(InputMethodManager.SHOW_IMPLICIT),
+                true /* expected */,
                 true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
 
-        // IME request to hide itself with flag: HIDE_IMPLICIT_ONLY, expect hidden.
+        // IME request to hide itself with flag HIDE_IMPLICIT_ONLY, expect hidden.
         Log.i(TAG, "Call IMS#requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY)");
         verifyInputViewStatusOnMainSync(
                 () -> mInputMethodService.requestHideSelf(InputMethodManager.HIDE_IMPLICIT_ONLY),
+                true /* expected */,
                 false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
     }
 
+    /**
+     * This checks the return value of IMS#onEvaluateInputViewShown,
+     * when show_ime_with_hard_keyboard is enabled.
+     */
     @Test
     public void testOnEvaluateInputViewShown_showImeWithHardKeyboard() throws Exception {
-        executeShellCommand(ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
-        mInstrumentation.waitForIdleSync();
+        setShowImeWithHardKeyboard(true /* enabled */);
 
-        // Simulate connecting a hard keyboard
         mInputMethodService.getResources().getConfiguration().keyboard =
                 Configuration.KEYBOARD_QWERTY;
         mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                 Configuration.HARDKEYBOARDHIDDEN_NO;
+        eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
 
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_NOKEYS;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_NO;
+        eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
+
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_QWERTY;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
         eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
     }
 
+    /**
+     * This checks the return value of IMSonEvaluateInputViewShown,
+     * when show_ime_with_hard_keyboard is disabled.
+     */
     @Test
-    public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() {
+    public void testOnEvaluateInputViewShown_disableShowImeWithHardKeyboard() throws Exception {
+        setShowImeWithHardKeyboard(false /* enabled */);
+
         mInputMethodService.getResources().getConfiguration().keyboard =
                 Configuration.KEYBOARD_QWERTY;
         mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
@@ -196,6 +248,8 @@
 
         mInputMethodService.getResources().getConfiguration().keyboard =
                 Configuration.KEYBOARD_NOKEYS;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_NO;
         eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
 
         mInputMethodService.getResources().getConfiguration().keyboard =
@@ -205,149 +259,386 @@
         eventually(() -> assertThat(mInputMethodService.onEvaluateInputViewShown()).isTrue());
     }
 
+    /**
+     * This checks that any (implicit or explicit) show request,
+     * when IMS#onEvaluateInputViewShown returns false, results in the IME not being shown.
+     */
     @Test
     public void testShowSoftInput_disableShowImeWithHardKeyboard() throws Exception {
-        // Simulate connecting a hard keyboard
+        setShowImeWithHardKeyboard(false /* enabled */);
+
+        // Simulate connecting a hard keyboard.
         mInputMethodService.getResources().getConfiguration().keyboard =
                 Configuration.KEYBOARD_QWERTY;
         mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
                 Configuration.HARDKEYBOARDHIDDEN_NO;
+
         // When InputMethodService#onEvaluateInputViewShown() returns false, the Ime should not be
         // shown no matter what the show flag is.
         verifyInputViewStatusOnMainSync(() -> assertThat(
                 mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+                false /* expected */,
                 false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
+
         verifyInputViewStatusOnMainSync(
                 () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                false /* expected */,
                 false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
     }
 
+    /**
+     * This checks that an explicit show request results in the IME being shown.
+     */
     @Test
     public void testShowSoftInputExplicitly() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
         // When InputMethodService#onEvaluateInputViewShown() returns true and flag is EXPLICIT, the
         // Ime should be shown.
         verifyInputViewStatusOnMainSync(
                 () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                true /* expected */,
                 true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
     }
 
+    /**
+     * This checks that an implicit show request results in the IME being shown.
+     */
     @Test
     public void testShowSoftInputImplicitly() throws Exception {
-        // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT, the
-        // Ime should be shown.
+        setShowImeWithHardKeyboard(true /* enabled */);
+
+        // When InputMethodService#onEvaluateInputViewShown() returns true and flag is IMPLICIT,
+        // the IME should be shown.
         verifyInputViewStatusOnMainSync(() -> assertThat(
                 mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+                true /* expected */,
                 true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
     }
 
+    /**
+     * This checks that an explicit show request when the IME is not previously shown,
+     * and it should be shown in fullscreen mode, results in the IME being shown.
+     */
     @Test
-    public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
-        // When keyboard is off, InputMethodService#onEvaluateInputViewShown returns true, flag is
-        // IMPLICIT and InputMethodService#onEvaluateFullScreenMode returns true, the Ime should not
-        // be shown.
+    public void testShowSoftInputExplicitly_fullScreenMode() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
+        // Set orientation landscape to enable fullscreen mode.
         setOrientation(2);
         eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse());
-        // Wait for the TestActivity to be recreated
+        // Wait for the TestActivity to be recreated.
         eventually(() ->
                 assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity));
-        // Get the new TestActivity
+        // Get the new TestActivity.
         mActivity = TestActivity.getLastCreatedInstance();
         assertThat(mActivity).isNotNull();
         InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
-        // Wait for the new EditText to be served by InputMethodManager
-        eventually(() ->
-                assertThat(imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
+        // Wait for the new EditText to be served by InputMethodManager.
+        eventually(() -> assertThat(
+                imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
+
         verifyInputViewStatusOnMainSync(() -> assertThat(
-                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
-                false /* inputViewStarted */);
+                        mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
     }
 
+    /**
+     * This checks that an implicit show request when the IME is not previously shown,
+     * and it should be shown in fullscreen mode, results in the IME not being shown.
+     */
+    @Test
+    public void testShowSoftInputImplicitly_fullScreenMode() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
+        // Set orientation landscape to enable fullscreen mode.
+        setOrientation(2);
+        eventually(() -> assertThat(mUiDevice.isNaturalOrientation()).isFalse());
+        // Wait for the TestActivity to be recreated.
+        eventually(() ->
+                assertThat(TestActivity.getLastCreatedInstance()).isNotEqualTo(mActivity));
+        // Get the new TestActivity.
+        mActivity = TestActivity.getLastCreatedInstance();
+        assertThat(mActivity).isNotNull();
+        InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+        // Wait for the new EditText to be served by InputMethodManager.
+        eventually(() -> assertThat(
+                imm.hasActiveInputConnection(mActivity.getEditText())).isTrue());
+
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+                false /* expected */,
+                false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
+    }
+
+    /**
+     * This checks that an explicit show request when a hard keyboard is connected,
+     * results in the IME being shown.
+     */
+    @Test
+    public void testShowSoftInputExplicitly_withHardKeyboard() throws Exception {
+        setShowImeWithHardKeyboard(false /* enabled */);
+
+        // Simulate connecting a hard keyboard.
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_QWERTY;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+    }
+
+    /**
+     * This checks that an implicit show request when a hard keyboard is connected,
+     * results in the IME not being shown.
+     */
     @Test
     public void testShowSoftInputImplicitly_withHardKeyboard() throws Exception {
+        setShowImeWithHardKeyboard(false /* enabled */);
+
+        // Simulate connecting a hard keyboard.
         mInputMethodService.getResources().getConfiguration().keyboard =
                 Configuration.KEYBOARD_QWERTY;
-        // When connecting to a hard keyboard and the flag is IMPLICIT, the Ime should not be shown.
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
         verifyInputViewStatusOnMainSync(() -> assertThat(
                 mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+                false /* expected */,
                 false /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isFalse();
     }
 
+    /**
+     * This checks that an explicit show request followed by connecting a hard keyboard
+     * and a configuration change, still results in the IME being shown.
+     */
     @Test
-    public void testConfigurationChanged_withKeyboardShownExplicitly() throws InterruptedException {
+    public void testShowSoftInputExplicitly_thenConfigurationChanged() throws Exception {
+        setShowImeWithHardKeyboard(false /* enabled */);
+
+        // Start with no hard keyboard.
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_NOKEYS;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
         verifyInputViewStatusOnMainSync(
                 () -> assertThat(mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                true /* expected */,
                 true /* inputViewStarted */);
-        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
-        mInputMethodService.getResources().getConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
-        verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
-                mInputMethodService.getResources().getConfiguration()),
-                true /* inputViewStarted */);
-    }
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
 
-    @Test
-    public void testConfigurationChanged_withKeyboardShownImplicitly() throws InterruptedException {
-        verifyInputViewStatusOnMainSync(() -> assertThat(
-                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
-                true /* inputViewStarted */);
-        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
-        mInputMethodService.getResources().getConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
+        // Simulate connecting a hard keyboard.
         mInputMethodService.getResources().getConfiguration().keyboard =
                 Configuration.KEYBOARD_QWERTY;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
+        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
+        mInputMethodService.getResources().getConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
+
+        verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
+                mInputMethodService.getResources().getConfiguration()),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+    }
+
+    /**
+     * This checks that an implicit show request followed by connecting a hard keyboard
+     * and a configuration change, does not trigger IMS#onFinishInputView,
+     * but results in the IME being hidden.
+     */
+    @Test
+    public void testShowSoftInputImplicitly_thenConfigurationChanged() throws Exception {
+        setShowImeWithHardKeyboard(false /* enabled */);
+
+        // Start with no hard keyboard.
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_NOKEYS;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+        // Simulate connecting a hard keyboard.
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_QWERTY;
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
+        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
+        mInputMethodService.getResources().getConfiguration().orientation =
+                Configuration.ORIENTATION_LANDSCAPE;
 
         // Normally, IMS#onFinishInputView will be called when finishing the input view by the user.
         // But if IMS#hideWindow is called when receiving a new configuration change, we don't
         // expect that it's user-driven to finish the lifecycle of input view with
         // IMS#onFinishInputView, because the input view will be re-initialized according to the
-        // last mShowSoftRequested state. So in this case we treat the input view is still alive.
+        // last #mShowInputRequested state. So in this case we treat the input view as still alive.
         verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
-                                mInputMethodService.getResources().getConfiguration()),
+                mInputMethodService.getResources().getConfiguration()),
+                true /* expected */,
                 true /* inputViewStarted */);
         assertThat(mInputMethodService.isInputViewShown()).isFalse();
     }
 
-    private void verifyInputViewStatus(Runnable runnable, boolean inputViewStarted)
-            throws InterruptedException {
-        verifyInputViewStatusInternal(runnable, inputViewStarted, false /*runOnMainSync*/);
+    /**
+     * This checks that an explicit show request directly followed by an implicit show request,
+     * while a hardware keyboard is connected, still results in the IME being shown
+     * (i.e. the implicit show request is treated as explicit).
+     */
+    @Test
+    public void testShowSoftInputExplicitly_thenShowSoftInputImplicitly_withHardKeyboard()
+            throws Exception {
+        setShowImeWithHardKeyboard(false /* enabled */);
+
+        // Simulate connecting a hard keyboard.
+        mInputMethodService.getResources().getConfiguration().keyboard =
+                Configuration.KEYBOARD_QWERTY;
+        mInputMethodService.getResources().getConfiguration().hardKeyboardHidden =
+                Configuration.HARDKEYBOARDHIDDEN_YES;
+
+        // Explicit show request.
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                        mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+        // Implicit show request.
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_IMPLICIT)).isTrue(),
+                false /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+        // Simulate a fake configuration change to avoid triggering the recreation of TestActivity.
+        // This should now consider the implicit show request, but keep the state from the
+        // explicit show request, and thus not hide the keyboard.
+        verifyInputViewStatusOnMainSync(() -> mInputMethodService.onConfigurationChanged(
+                        mInputMethodService.getResources().getConfiguration()),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
     }
 
-    private void verifyInputViewStatusOnMainSync(Runnable runnable, boolean inputViewStarted)
-            throws InterruptedException {
-        verifyInputViewStatusInternal(runnable, inputViewStarted, true /*runOnMainSync*/);
+    /**
+     * This checks that a forced show request directly followed by an explicit show request,
+     * and then a hide not always request, still results in the IME being shown
+     * (i.e. the explicit show request retains the forced state).
+     */
+    @Test
+    public void testShowSoftInputForced_testShowSoftInputExplicitly_thenHideSoftInputNotAlways()
+            throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                mActivity.showImeWithInputMethodManager(InputMethodManager.SHOW_FORCED)).isTrue(),
+                true /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+        verifyInputViewStatusOnMainSync(() -> assertThat(
+                        mActivity.showImeWithInputMethodManager(0 /* flags */)).isTrue(),
+                false /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
+
+        verifyInputViewStatusOnMainSync(() ->
+                        mActivity.hideImeWithInputMethodManager(InputMethodManager.HIDE_NOT_ALWAYS),
+                false /* expected */,
+                true /* inputViewStarted */);
+        assertThat(mInputMethodService.isInputViewShown()).isTrue();
     }
 
+    /**
+     * This checks that the IME fullscreen mode state is updated after changing orientation.
+     */
+    @Test
+    public void testFullScreenMode() throws Exception {
+        setShowImeWithHardKeyboard(true /* enabled */);
+
+        Log.i(TAG, "Set orientation natural");
+        verifyFullscreenMode(() -> setOrientation(0),
+                false /* expected */,
+                true /* orientationPortrait */);
+
+        Log.i(TAG, "Set orientation left");
+        verifyFullscreenMode(() -> setOrientation(1),
+                true /* expected */,
+                false /* orientationPortrait */);
+
+        Log.i(TAG, "Set orientation right");
+        verifyFullscreenMode(() -> setOrientation(2),
+                false /* expected */,
+                false /* orientationPortrait */);
+    }
+
+    private void verifyInputViewStatus(
+            Runnable runnable, boolean expected, boolean inputViewStarted)
+            throws InterruptedException {
+        verifyInputViewStatusInternal(runnable, expected, inputViewStarted,
+                false /* runOnMainSync */);
+    }
+
+    private void verifyInputViewStatusOnMainSync(
+            Runnable runnable, boolean expected, boolean inputViewStarted)
+            throws InterruptedException {
+        verifyInputViewStatusInternal(runnable, expected, inputViewStarted,
+                true /* runOnMainSync */);
+    }
+
+    /**
+     * Verifies the status of the Input View after executing the given runnable.
+     *
+     * @param runnable the runnable to execute for showing or hiding the IME.
+     * @param expected whether the runnable is expected to trigger the signal.
+     * @param inputViewStarted the expected state of the Input View after executing the runnable.
+     * @param runOnMainSync whether to execute the runnable on the main thread.
+     */
     private void verifyInputViewStatusInternal(
-            Runnable runnable, boolean inputViewStarted, boolean runOnMainSync)
+            Runnable runnable, boolean expected, boolean inputViewStarted, boolean runOnMainSync)
             throws InterruptedException {
         CountDownLatch signal = new CountDownLatch(1);
         mInputMethodService.setCountDownLatchForTesting(signal);
-        // Runnable to trigger onStartInputView()/ onFinishInputView()
+        // Runnable to trigger onStartInputView() / onFinishInputView() / onConfigurationChanged()
         if (runOnMainSync) {
             mInstrumentation.runOnMainSync(runnable);
         } else {
             runnable.run();
         }
-        // Waits for onStartInputView() to finish.
         mInstrumentation.waitForIdleSync();
-        signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+        boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+        if (expected && !completed) {
+            fail("Timed out waiting for"
+                    + " onStartInputView() / onFinishInputView() / onConfigurationChanged()");
+        } else if (!expected && completed) {
+            fail("Unexpected call"
+                    + " onStartInputView() / onFinishInputView() / onConfigurationChanged()");
+        }
         // Input is not finished.
         assertThat(mInputMethodService.getCurrentInputStarted()).isTrue();
         assertThat(mInputMethodService.getCurrentInputViewStarted()).isEqualTo(inputViewStarted);
     }
 
-    @Test
-    public void testFullScreenMode() throws Exception {
-        Log.i(TAG, "Set orientation natural");
-        verifyFullscreenMode(() -> setOrientation(0), true /* orientationPortrait */);
-
-        Log.i(TAG, "Set orientation left");
-        verifyFullscreenMode(() -> setOrientation(1), false /* orientationPortrait */);
-
-        Log.i(TAG, "Set orientation right");
-        verifyFullscreenMode(() -> setOrientation(2), false /* orientationPortrait */);
-    }
-
     private void setOrientation(int orientation) {
         // Simple wrapper for catching RemoteException.
         try {
@@ -366,7 +657,15 @@
         }
     }
 
-    private void verifyFullscreenMode(Runnable runnable, boolean orientationPortrait)
+    /**
+     * Verifies the IME fullscreen mode state after executing the given runnable.
+     *
+     * @param runnable the runnable to execute for setting the orientation.
+     * @param expected whether the runnable is expected to trigger the signal.
+     * @param orientationPortrait whether the orientation is expected to be portrait.
+     */
+    private void verifyFullscreenMode(
+            Runnable runnable, boolean expected, boolean orientationPortrait)
             throws InterruptedException {
         CountDownLatch signal = new CountDownLatch(1);
         mInputMethodService.setCountDownLatchForTesting(signal);
@@ -379,7 +678,12 @@
         }
         // Waits for onConfigurationChanged() to finish.
         mInstrumentation.waitForIdleSync();
-        signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+        boolean completed = signal.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+        if (expected && !completed) {
+            fail("Timed out waiting for onConfigurationChanged()");
+        } else if (!expected && completed) {
+            fail("Unexpected call onConfigurationChanged()");
+        }
 
         clickOnEditorText();
         eventually(() -> assertThat(mInputMethodService.isInputViewShown()).isTrue());
@@ -416,7 +720,21 @@
         return mTargetPackageName + "/" + INPUT_METHOD_SERVICE_NAME;
     }
 
-    private String executeShellCommand(String cmd) throws Exception {
+    /**
+     * Sets the value of show_ime_with_hard_keyboard, only if it is different to the default value.
+     *
+     * @param enabled the value to be set.
+     */
+    private void setShowImeWithHardKeyboard(boolean enabled) throws IOException {
+        if (mShowImeWithHardKeyboardEnabled != enabled) {
+            executeShellCommand(enabled
+                    ? ENABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD
+                    : DISABLE_SHOW_IME_WITH_HARD_KEYBOARD_CMD);
+            mInstrumentation.waitForIdleSync();
+        }
+    }
+
+    private String executeShellCommand(String cmd) throws IOException {
         Log.i(TAG, "Run command: " + cmd);
         return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
                 .executeShellCommand(cmd);
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index 869497c..3199e06 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -40,7 +40,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.Display;
-import android.view.inputmethod.InputMethodManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -77,9 +76,9 @@
     public void testPerformShowIme() throws Exception {
         synchronized (ImfLock.class) {
             mVisibilityApplier.performShowIme(new Binder() /* showInputToken */,
-                    null /* statsToken */, InputMethodManager.SHOW_IMPLICIT, null, SHOW_SOFT_INPUT);
+                    null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT);
         }
-        verifyShowSoftInput(false, true, InputMethodManager.SHOW_IMPLICIT);
+        verifyShowSoftInput(false, true, 0 /* showFlags */);
     }
 
     @Test
@@ -126,7 +125,7 @@
     @Test
     public void testApplyImeVisibility_showImeImplicit() throws Exception {
         mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT);
-        verifyShowSoftInput(true, true, InputMethodManager.SHOW_IMPLICIT);
+        verifyShowSoftInput(true, true, 0 /* showFlags */);
     }
 
     @Test
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
index a38c162..fae5f86 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ImeVisibilityStateComputerTest.java
@@ -106,7 +106,7 @@
     @Test
     public void testRequestImeVisibility_showExplicit() {
         initImeTargetWindowState(mWindowToken);
-        boolean res = mComputer.onImeShowFlags(null, 0 /* show explicit */);
+        boolean res = mComputer.onImeShowFlags(null, 0 /* showFlags */);
         mComputer.requestImeVisibility(mWindowToken, res);
 
         final ImeTargetWindowState state = mComputer.getWindowStateOrNull(mWindowToken);
@@ -118,6 +118,34 @@
         assertThat(mComputer.mRequestedShowExplicitly).isTrue();
     }
 
+    /**
+     * This checks that the state after an explicit show request does not get reset during
+     * a subsequent implicit show request, without an intermediary hide request.
+     */
+    @Test
+    public void testRequestImeVisibility_showExplicit_thenShowImplicit() {
+        initImeTargetWindowState(mWindowToken);
+        mComputer.onImeShowFlags(null, 0 /* showFlags */);
+        assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+
+        mComputer.onImeShowFlags(null, InputMethodManager.SHOW_IMPLICIT);
+        assertThat(mComputer.mRequestedShowExplicitly).isTrue();
+    }
+
+    /**
+     * This checks that the state after a forced show request does not get reset during
+     * a subsequent explicit show request, without an intermediary hide request.
+     */
+    @Test
+    public void testRequestImeVisibility_showForced_thenShowExplicit() {
+        initImeTargetWindowState(mWindowToken);
+        mComputer.onImeShowFlags(null, InputMethodManager.SHOW_FORCED);
+        assertThat(mComputer.mShowForced).isTrue();
+
+        mComputer.onImeShowFlags(null, 0 /* showFlags */);
+        assertThat(mComputer.mShowForced).isTrue();
+    }
+
     @Test
     public void testRequestImeVisibility_showImplicit_a11yNoImePolicy() {
         // Precondition: set AccessibilityService#SHOW_MODE_HIDDEN policy
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
index 42d373b..e87a34e 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodBindingControllerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -162,7 +163,10 @@
             assertThat(mBindingController.getCurToken()).isNotNull();
         }
         // Wait for onServiceConnected()
-        mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+        boolean completed = mCountDownLatch.await(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+        if (!completed) {
+            fail("Timed out waiting for onServiceConnected()");
+        }
 
         // Verify onServiceConnected() is called and bound successfully.
         synchronized (ImfLock.class) {
diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp
index 8d0e0c4..e1fd2b3 100644
--- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/Android.bp
@@ -43,6 +43,8 @@
     },
     export_package_resources: true,
     sdk_version: "current",
+
+    certificate: "platform",
 }
 
 android_library {
diff --git a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml
index 996322d..cf7d660 100644
--- a/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml
+++ b/services/tests/InputMethodSystemServerTests/test-apps/SimpleTestIme/AndroidManifest.xml
@@ -18,8 +18,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.apps.inputmethod.simpleime">
 
-    <uses-sdk android:targetSdkVersion="31" />
-
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <application android:debuggable="true"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index 95c62ae..7e69357 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -86,7 +86,9 @@
                 .append("\n    brightnessReason:")
                 .append(displayBrightnessState.getBrightnessReason())
                 .append("\n    shouldUseAutoBrightness:")
-                .append(displayBrightnessState.getShouldUseAutoBrightness());
+                .append(displayBrightnessState.getShouldUseAutoBrightness())
+                .append("\n    isSlowChange:")
+                .append(displayBrightnessState.isSlowChange());
         return sb.toString();
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index 081f19d..d8569f7 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -21,8 +21,8 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.view.Display;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.display.DisplayBrightnessState;
 import com.android.server.display.brightness.BrightnessReason;
@@ -46,7 +46,8 @@
         DisplayManagerInternal.DisplayPowerRequest
                 displayPowerRequest = new DisplayManagerInternal.DisplayPowerRequest();
         float brightnessToFollow = 0.2f;
-        mFollowerBrightnessStrategy.setBrightnessToFollow(brightnessToFollow);
+        boolean slowChange = true;
+        mFollowerBrightnessStrategy.setBrightnessToFollow(brightnessToFollow, slowChange);
         BrightnessReason brightnessReason = new BrightnessReason();
         brightnessReason.setReason(BrightnessReason.REASON_FOLLOWER);
         DisplayBrightnessState expectedDisplayBrightnessState =
@@ -55,6 +56,7 @@
                         .setBrightnessReason(brightnessReason)
                         .setSdrBrightness(brightnessToFollow)
                         .setDisplayBrightnessStrategyName(mFollowerBrightnessStrategy.getName())
+                        .setIsSlowChange(slowChange)
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mFollowerBrightnessStrategy.updateBrightness(displayPowerRequest);
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
new file mode 100644
index 0000000..b698a60
--- /dev/null
+++ b/services/tests/dreamservicetests/Android.bp
@@ -0,0 +1,35 @@
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "DreamServiceTests",
+
+    // Include all test java files.
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.ext.truth",
+        "frameworks-base-testutils",
+        "mockito-target-minus-junit4",
+        "services.core",
+    ],
+
+    platform_apis: true,
+
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+
+    certificate: "platform",
+
+    dxflags: ["--multi-dex"],
+
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/services/tests/dreamservicetests/AndroidManifest.xml b/services/tests/dreamservicetests/AndroidManifest.xml
new file mode 100644
index 0000000..fc3ad34
--- /dev/null
+++ b/services/tests/dreamservicetests/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.frameworks.dreamservicetests">
+
+    <!--
+    Insert permissions here. eg:
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    -->
+
+    <application android:debuggable="true"
+                 android:testOnly="true">
+        <uses-library android:name="android.test.mock" android:required="true" />
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+         android:targetPackage="com.android.frameworks.dreamservicetests"
+         android:label="Frameworks Services Tests"/>
+</manifest>
diff --git a/services/tests/vibrator/AndroidTest.xml b/services/tests/dreamservicetests/AndroidTest.xml
similarity index 73%
rename from services/tests/vibrator/AndroidTest.xml
rename to services/tests/dreamservicetests/AndroidTest.xml
index d5ee3af..ae89281 100644
--- a/services/tests/vibrator/AndroidTest.xml
+++ b/services/tests/dreamservicetests/AndroidTest.xml
@@ -13,22 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<configuration description="Runs Frameworks Vibrator Services Tests.">
+<configuration description="Runs Dream Service Tests.">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-instrumentation" />
 
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
-        <option name="test-file-name" value="FrameworksVibratorServicesTests.apk" />
+        <option name="test-file-name" value="DreamServiceTests.apk" />
     </target_preparer>
 
-    <option name="test-tag" value="FrameworksVibratorServicesTests" />
-
-    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
-        <option name="package" value="com.android.framework.services.tests.vibrator" />
+    <option name="test-tag" value="DreamServiceTests" />
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.frameworks.dreamservicetests" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
-        <option name="hidden-api-checks" value="false" />
+        <option name="hidden-api-checks" value="false"/>
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
     </test>
 </configuration>
diff --git a/services/tests/dreamservicetests/OWNERS b/services/tests/dreamservicetests/OWNERS
new file mode 100644
index 0000000..6dd64e7
--- /dev/null
+++ b/services/tests/dreamservicetests/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 78010
+
+include /core/java/android/service/dreams/OWNERS
diff --git a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
+++ b/services/tests/mockingservicestests/assets/AppOpsUpgradeTest/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
 include /core/java/android/permission/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 72c5333..410ae35 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -328,7 +328,7 @@
                 eq(ActivityManager.PROCESS_STATE_LAST_ACTIVITY), any());
 
         mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
-        mConstants.TIMEOUT = 100;
+        mConstants.TIMEOUT = 200;
         mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
         mConstants.PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 500;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS b/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
 include /core/java/android/permission/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
index 1ae9124..52044bf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
@@ -368,10 +369,11 @@
         File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
         ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
                 /* isFactory= */ false, finalApex);
-        when(mApexService.installAndActivatePackage(anyString())).thenReturn(newApexInfo);
+        when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenReturn(
+                newApexInfo);
 
         File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex");
-        newApexInfo = mApexManager.installPackage(installedApex);
+        newApexInfo = mApexManager.installPackage(installedApex, /* force= */ false);
 
         var newPkg = mockParsePackage(mPackageParser2, newApexInfo);
         assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
@@ -398,10 +400,11 @@
         File finalApex = extractResource("test.rebootles_apex_v2", "test.rebootless_apex_v2.apex");
         ApexInfo newApexInfo = createApexInfo("test.apex_rebootless", 2, /* isActive= */ true,
                 /* isFactory= */ false, finalApex);
-        when(mApexService.installAndActivatePackage(anyString())).thenReturn(newApexInfo);
+        when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenReturn(
+                newApexInfo);
 
         File installedApex = extractResource("installed", "test.rebootless_apex_v2.apex");
-        newApexInfo = mApexManager.installPackage(installedApex);
+        newApexInfo = mApexManager.installPackage(installedApex, /* force= */ false);
 
         var newPkg = mockParsePackage(mPackageParser2, newApexInfo);
         assertThat(newPkg.getBaseApkPath()).isEqualTo(finalApex.getAbsolutePath());
@@ -416,13 +419,13 @@
 
     @Test
     public void testInstallPackageBinderCallFails() throws Exception {
-        when(mApexService.installAndActivatePackage(anyString())).thenThrow(
+        when(mApexService.installAndActivatePackage(anyString(), anyBoolean())).thenThrow(
                 new RuntimeException("install failed :("));
 
         File installedApex = extractResource("test.apex_rebootless_v1",
                 "test.rebootless_apex_v1.apex");
         assertThrows(PackageManagerException.class,
-                () -> mApexManager.installPackage(installedApex));
+                () -> mApexManager.installPackage(installedApex, /* force= */ false));
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
index 7b16545..a8b0a7b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/ArchiveManagerTest.java
@@ -40,6 +40,7 @@
 import android.os.Process;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.text.TextUtils;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -169,14 +170,14 @@
     }
 
     @Test
-    public void archiveApp_callerNotInstallerOfRecord() {
+    public void archiveApp_noInstallerFound() {
         InstallSource otherInstallSource =
                 InstallSource.create(
                         CALLER_PACKAGE,
                         CALLER_PACKAGE,
-                        /* installerPackageName= */ "different",
+                        /* installerPackageName= */ null,
                         Binder.getCallingUid(),
-                        CALLER_PACKAGE,
+                        /* updateOwnerPackageName= */ null,
                         /* installerAttributionTag= */ null,
                         /* packageSource= */ 0);
         when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
@@ -187,29 +188,8 @@
                         mIntentSender)
         );
         assertThat(e).hasMessageThat().isEqualTo(
-                String.format("Caller is not the installer of record for %s.", PACKAGE));
-    }
-
-    @Test
-    public void archiveApp_callerNotUpdateOwner() {
-        InstallSource otherInstallSource =
-                InstallSource.create(
-                        CALLER_PACKAGE,
-                        CALLER_PACKAGE,
-                        CALLER_PACKAGE,
-                        Binder.getCallingUid(),
-                        /* updateOwnerPackageName= */ "different",
-                        /* installerAttributionTag= */ null,
-                        /* packageSource= */ 0);
-        when(mPackageState.getInstallSource()).thenReturn(otherInstallSource);
-
-        Exception e = assertThrows(
-                SecurityException.class,
-                () -> mArchiveManager.archiveApp(PACKAGE, CALLER_PACKAGE, UserHandle.CURRENT,
-                        mIntentSender)
-        );
-        assertThat(e).hasMessageThat().isEqualTo(
-                String.format("Caller is not the update owner for %s.", PACKAGE));
+                TextUtils.formatSimple("No installer found to archive app %s.",
+                        PACKAGE));
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
index 3ac59e9..f206333 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java
@@ -33,6 +33,8 @@
 import android.os.Handler;
 import android.os.IRemoteCallback;
 import android.os.Looper;
+import android.os.Process;
+import android.util.SparseArray;
 
 import org.junit.After;
 import org.junit.Before;
@@ -244,6 +246,54 @@
         verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
     }
 
+    @Test
+    public void testRegisterPackageMonitorCallbackInAllowList_callbackShouldCalled()
+            throws Exception {
+        IRemoteCallback callback = createMockPackageMonitorCallback();
+        SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+        broadcastAllowList.put(0, new int[] {Binder.getCallingUid()});
+
+        mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+                Binder.getCallingUid());
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+                FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+                null /* instantUserIds */, broadcastAllowList);
+
+        verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+    }
+
+    @Test
+    public void testRegisterPackageMonitorCallbackNotInAllowList_callbackShouldNotCalled()
+            throws Exception {
+        IRemoteCallback callback = createMockPackageMonitorCallback();
+        SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+        broadcastAllowList.put(0, new int[] {12345});
+
+        mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+                Binder.getCallingUid());
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+                FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+                null /* instantUserIds */, broadcastAllowList);
+
+        verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any());
+    }
+
+    @Test
+    public void testRegisterPackageMonitorCallbackNotInAllowListSystemUid_callbackShouldCalled()
+            throws Exception {
+        IRemoteCallback callback = createMockPackageMonitorCallback();
+        SparseArray<int[]> broadcastAllowList = new SparseArray<>();
+        broadcastAllowList.put(0, new int[] {12345});
+
+        mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */,
+                Process.SYSTEM_UID);
+        mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED,
+                FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */,
+                null /* instantUserIds */, broadcastAllowList);
+
+        verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any());
+    }
+
     private IRemoteCallback createMockPackageMonitorCallback() {
         return spy(new IRemoteCallback.Stub() {
             @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
deleted file mode 100644
index 0f2c27c..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.power;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManagerInternal;
-import android.attention.AttentionManagerInternal;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.res.Resources;
-import android.hardware.SensorManager;
-import android.hardware.devicestate.DeviceStateManager;
-import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.display.DisplayManagerInternal;
-import android.os.BatteryManagerInternal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.PowerSaveState;
-import android.os.test.TestLooper;
-import android.provider.Settings;
-import android.service.dreams.DreamManagerInternal;
-import android.test.mock.MockContentResolver;
-import android.view.Display;
-import android.view.DisplayInfo;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.internal.app.IBatteryStats;
-import com.android.internal.util.test.FakeSettingsProvider;
-import com.android.server.LocalServices;
-import com.android.server.SystemService;
-import com.android.server.lights.LightsManager;
-import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.power.PowerManagerService.BatteryReceiver;
-import com.android.server.power.PowerManagerService.Injector;
-import com.android.server.power.PowerManagerService.NativeWrapper;
-import com.android.server.power.PowerManagerService.UserSwitchedReceiver;
-import com.android.server.power.batterysaver.BatterySaverController;
-import com.android.server.power.batterysaver.BatterySaverPolicy;
-import com.android.server.power.batterysaver.BatterySaverStateMachine;
-import com.android.server.power.batterysaver.BatterySavingStats;
-import com.android.server.testutils.OffsettableClock;
-
-import java.util.concurrent.Executor;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link com.android.server.power.PowerManagerService}.
- *
- * Build/Install/Run:
- *  atest FrameworksServicesTests:PowerManagerServiceMockingTest
- */
-public class PowerManagerServiceMockingTest {
-    private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
-    private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
-
-    private static final float BRIGHTNESS_FACTOR = 0.7f;
-    private static final boolean BATTERY_SAVER_ENABLED = true;
-
-    @Mock private BatterySaverController mBatterySaverControllerMock;
-    @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
-    @Mock private BatterySaverStateMachine mBatterySaverStateMachineMock;
-    @Mock private LightsManager mLightsManagerMock;
-    @Mock private DisplayManagerInternal mDisplayManagerInternalMock;
-    @Mock private BatteryManagerInternal mBatteryManagerInternalMock;
-    @Mock private ActivityManagerInternal mActivityManagerInternalMock;
-    @Mock private AttentionManagerInternal mAttentionManagerInternalMock;
-    @Mock private DreamManagerInternal mDreamManagerInternalMock;
-    @Mock private PowerManagerService.NativeWrapper mNativeWrapperMock;
-    @Mock private Notifier mNotifierMock;
-    @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
-    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
-    @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
-    @Mock private DeviceStateManager mDeviceStateManagerMock;
-
-    @Mock
-    private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
-
-    private PowerManagerService mService;
-    private PowerSaveState mPowerSaveState;
-    private ContextWrapper mContextSpy;
-    private BatteryReceiver mBatteryReceiver;
-    private UserSwitchedReceiver mUserSwitchedReceiver;
-    private Resources mResourcesSpy;
-    private OffsettableClock mClock;
-    private TestLooper mTestLooper;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        FakeSettingsProvider.clearSettingsProvider();
-
-        mPowerSaveState = new PowerSaveState.Builder()
-                .setBatterySaverEnabled(BATTERY_SAVER_ENABLED)
-                .setBrightnessFactor(BRIGHTNESS_FACTOR)
-                .build();
-        when(mBatterySaverPolicyMock.getBatterySaverPolicy(
-                eq(PowerManager.ServiceType.SCREEN_BRIGHTNESS)))
-                .thenReturn(mPowerSaveState);
-        when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
-        when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
-        when(mDisplayManagerInternalMock.requestPowerState(anyInt(), any(), anyBoolean()))
-                .thenReturn(true);
-        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
-        when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
-
-        addLocalServiceMock(LightsManager.class, mLightsManagerMock);
-        addLocalServiceMock(DisplayManagerInternal.class, mDisplayManagerInternalMock);
-        addLocalServiceMock(BatteryManagerInternal.class, mBatteryManagerInternalMock);
-        addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock);
-        addLocalServiceMock(AttentionManagerInternal.class, mAttentionManagerInternalMock);
-        addLocalServiceMock(DreamManagerInternal.class, mDreamManagerInternalMock);
-
-        mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
-        mResourcesSpy = spy(mContextSpy.getResources());
-        when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
-
-        MockContentResolver cr = new MockContentResolver(mContextSpy);
-        cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        when(mContextSpy.getContentResolver()).thenReturn(cr);
-
-        when(mContextSpy.getSystemService(DeviceStateManager.class))
-                .thenReturn(mDeviceStateManagerMock);
-
-        Settings.Global.putInt(mContextSpy.getContentResolver(),
-                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
-
-        mClock = new OffsettableClock.Stopped();
-        mTestLooper = new TestLooper(mClock::now);
-    }
-
-    private PowerManagerService createService() {
-        mService = new PowerManagerService(mContextSpy, new Injector() {
-            @Override
-            Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats,
-                    SuspendBlocker suspendBlocker, WindowManagerPolicy policy,
-                    FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector,
-                    Executor executor) {
-                return mNotifierMock;
-            }
-
-            @Override
-            SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) {
-                return super.createSuspendBlocker(service, name);
-            }
-
-            @Override
-            BatterySaverPolicy createBatterySaverPolicy(
-                    Object lock, Context context, BatterySavingStats batterySavingStats) {
-                return mBatterySaverPolicyMock;
-            }
-
-            @Override
-            BatterySaverController createBatterySaverController(
-                    Object lock, Context context, BatterySaverPolicy batterySaverPolicy,
-                    BatterySavingStats batterySavingStats) {
-                return mBatterySaverControllerMock;
-            }
-
-            @Override
-            BatterySaverStateMachine createBatterySaverStateMachine(Object lock, Context context,
-                    BatterySaverController batterySaverController) {
-                return mBatterySaverStateMachineMock;
-            }
-
-            @Override
-            NativeWrapper createNativeWrapper() {
-                return mNativeWrapperMock;
-            }
-
-            @Override
-            WirelessChargerDetector createWirelessChargerDetector(
-                    SensorManager sensorManager, SuspendBlocker suspendBlocker, Handler handler) {
-                return mWirelessChargerDetectorMock;
-            }
-
-            @Override
-            AmbientDisplayConfiguration createAmbientDisplayConfiguration(Context context) {
-                return mAmbientDisplayConfigurationMock;
-            }
-
-            @Override
-            InattentiveSleepWarningController createInattentiveSleepWarningController() {
-                return mInattentiveSleepWarningControllerMock;
-            }
-
-            @Override
-            public SystemPropertiesWrapper createSystemPropertiesWrapper() {
-                return mSystemPropertiesMock;
-            }
-
-            @Override
-            PowerManagerService.Clock createClock() {
-                return new PowerManagerService.Clock() {
-                    @Override
-                    public long uptimeMillis() {
-                        return mClock.now();
-                    }
-
-                    @Override
-                    public long elapsedRealtime() {
-                        return mClock.now();
-                    }
-                };
-            }
-
-            @Override
-            Handler createHandler(Looper looper, Handler.Callback callback) {
-                return new Handler(mTestLooper.getLooper(), callback);
-            }
-
-            @Override
-            void invalidateIsInteractiveCaches() {
-                // Avoids an SELinux failure.
-            }
-        });
-        return mService;
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        LocalServices.removeServiceForTest(LightsManager.class);
-        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
-        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
-        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-        LocalServices.removeServiceForTest(AttentionManagerInternal.class);
-        LocalServices.removeServiceForTest(DreamManagerInternal.class);
-        FakeSettingsProvider.clearSettingsProvider();
-    }
-
-    /**
-     * Creates a mock and registers it to {@link LocalServices}.
-     */
-    private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
-        LocalServices.removeServiceForTest(clazz);
-        LocalServices.addService(clazz, mock);
-    }
-
-    private void advanceTime(long timeMs) {
-        mClock.fastForward(timeMs);
-        mTestLooper.dispatchAll();
-    }
-
-    @Test
-    public void testUserActivityOnDeviceStateChange() {
-        createService();
-        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
-
-        final DisplayInfo info = new DisplayInfo();
-        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
-        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
-
-        final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
-                ArgumentCaptor.forClass(DeviceStateCallback.class);
-        verify(mDeviceStateManagerMock).registerCallback(any(),
-                deviceStateCallbackCaptor.capture());
-
-        // Advance the time 10001 and verify that the device thinks it has been idle
-        // for just less than that.
-        mService.onUserActivity();
-        advanceTime(10001);
-        assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
-
-        // Send a display state change event and advance the clock 10.
-        final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
-        deviceStateCallback.onStateChanged(1);
-        final long timeToAdvance = 10;
-        advanceTime(timeToAdvance);
-
-        // Ensure that the device has been idle for only 10 (doesn't include the idle time
-        // before the display state event).
-        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
-        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
-
-        // Send the same state and ensure that does not trigger an update.
-        deviceStateCallback.onStateChanged(1);
-        advanceTime(timeToAdvance);
-        final long newTime = timeToAdvance * 2;
-
-        assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
-        assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
-    }
-}
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 236f90c..9384015 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -12,9 +12,19 @@
 
     static_libs: [
         "frameworks-base-testutils",
+        "platform-compat-test-rules",
         "platform-test-annotations",
         "services.core",
         "servicestests-utils",
+        "testables",
+    ],
+
+    libs: [
+        "android.test.mock",
+    ],
+
+    defaults: [
+        "modules-utils-testable-device-config-defaults",
     ],
 
     platform_apis: true,
diff --git a/services/tests/powerservicetests/AndroidManifest.xml b/services/tests/powerservicetests/AndroidManifest.xml
index 3ace9d5..26d9eec 100644
--- a/services/tests/powerservicetests/AndroidManifest.xml
+++ b/services/tests/powerservicetests/AndroidManifest.xml
@@ -17,10 +17,19 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.frameworks.powerservicetests">
 
-    <!--
-    Insert permissions here. eg:
-    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-    -->
+    <!-- Permissions -->
+    <uses-permission android:name="android.permission.DEVICE_POWER"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
+    <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+    <uses-permission android:name="android.permission.READ_DREAM_STATE"/>
+    <uses-permission android:name="android.permission.READ_DREAM_SUPPRESSION"/>
+    <uses-permission android:name="android.permission.STATUS_BAR_SERVICE"/>
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS"/>
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.WRITE_DREAM_STATE"/>
+
     <application android:debuggable="true"
                  android:testOnly="true">
         <uses-library android:name="android.test.mock" android:required="true" />
diff --git a/services/tests/powerservicetests/OWNERS b/services/tests/powerservicetests/OWNERS
index e740577..aff0930 100644
--- a/services/tests/powerservicetests/OWNERS
+++ b/services/tests/powerservicetests/OWNERS
@@ -2,4 +2,3 @@
 
 include /services/core/java/com/android/server/power/OWNERS
 
-per-file ThermalManagerServiceTest.java=wvw@google.com, xwxw@google.com
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/LowPowerStandbyControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/NotifierTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/PowerGroupTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/PowerGroupTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
similarity index 91%
rename from services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
index c80ff45..9bce536 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/powerservicetests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -18,6 +18,8 @@
 
 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
 import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING;
 import static android.os.PowerManager.USER_ACTIVITY_EVENT_BUTTON;
 import static android.os.PowerManagerInternal.WAKEFULNESS_ASLEEP;
 import static android.os.PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -60,6 +62,8 @@
 import android.content.PermissionChecker;
 import android.content.res.Resources;
 import android.hardware.SensorManager;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
@@ -78,6 +82,7 @@
 import android.os.PowerSaveState;
 import android.os.UserHandle;
 import android.os.test.TestLooper;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
 import android.sysprop.PowerProperties;
@@ -92,6 +97,7 @@
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
 import com.android.server.lights.LightsManager;
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.power.PowerManagerService.BatteryReceiver;
@@ -160,6 +166,8 @@
     @Mock private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
     @Mock private PowerManagerService.PermissionCheckerWrapper mPermissionCheckerWrapperMock;
     @Mock private PowerManagerService.PowerPropertiesWrapper mPowerPropertiesWrapper;
+    @Mock private DeviceStateManager mDeviceStateManagerMock;
+    @Mock private DeviceConfigParameterProvider mDeviceParameterProvider;
 
     @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule();
 
@@ -341,6 +349,11 @@
             PowerManagerService.PowerPropertiesWrapper createPowerPropertiesWrapper() {
                 return mPowerPropertiesWrapper;
             }
+
+            @Override
+            DeviceConfigParameterProvider createDeviceConfigParameterProvider() {
+                return mDeviceParameterProvider;
+            }
         });
         return mService;
     }
@@ -2710,4 +2723,241 @@
         verify(mNotifierMock, never()).onUserActivity(anyInt(),  anyInt(), anyInt());
     }
 
+    @Test
+    public void testUserActivityOnDeviceStateChange() {
+        when(mContextSpy.getSystemService(DeviceStateManager.class))
+                .thenReturn(mDeviceStateManagerMock);
+
+        createService();
+        mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+
+        final DisplayInfo info = new DisplayInfo();
+        info.displayGroupId = Display.DEFAULT_DISPLAY_GROUP;
+        when(mDisplayManagerInternalMock.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn(info);
+
+        final ArgumentCaptor<DeviceStateCallback> deviceStateCallbackCaptor =
+                ArgumentCaptor.forClass(DeviceStateCallback.class);
+        verify(mDeviceStateManagerMock).registerCallback(any(),
+                deviceStateCallbackCaptor.capture());
+
+        // Advance the time 10001 and verify that the device thinks it has been idle
+        // for just less than that.
+        mService.onUserActivity();
+        advanceTime(10001);
+        assertThat(mService.wasDeviceIdleForInternal(10000)).isTrue();
+
+        // Send a display state change event and advance the clock 10.
+        final DeviceStateCallback deviceStateCallback = deviceStateCallbackCaptor.getValue();
+        deviceStateCallback.onStateChanged(1);
+        final long timeToAdvance = 10;
+        advanceTime(timeToAdvance);
+
+        // Ensure that the device has been idle for only 10 (doesn't include the idle time
+        // before the display state event).
+        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance - 1)).isTrue();
+        assertThat(mService.wasDeviceIdleForInternal(timeToAdvance)).isFalse();
+
+        // Send the same state and ensure that does not trigger an update.
+        deviceStateCallback.onStateChanged(1);
+        advanceTime(timeToAdvance);
+        final long newTime = timeToAdvance * 2;
+
+        assertThat(mService.wasDeviceIdleForInternal(newTime - 1)).isTrue();
+        assertThat(mService.wasDeviceIdleForInternal(newTime)).isFalse();
+    }
+
+    @Test
+    public void testFeatureEnabledProcStateUncachedToCached_fullWakeLockDisabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+
+        setCachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isTrue();
+    }
+
+    @Test
+    public void testFeatureDisabledProcStateUncachedToCached_fullWakeLockEnabled() {
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+
+        setCachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureEnabledProcStateUncachedToCached_screenBrightWakeLockDisabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+
+        setCachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isTrue();
+    }
+
+    @Test
+    public void testFeatureDisabledProcStateUncachedToCached_screenBrightWakeLockEnabled() {
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+
+        setCachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureEnabledProcStateUncachedToCached_screenDimWakeLockDisabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+                PowerManager.SCREEN_DIM_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+
+        setCachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isTrue();
+    }
+
+    @Test
+    public void testFeatureDisabledProcStateUncachedToCached_screenDimWakeLockEnabled() {
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+                PowerManager.SCREEN_DIM_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+
+        setCachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureEnabledProcStateCachedToUncached_fullWakeLockEnabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+        setCachedUidProcState(wakeLock.mOwnerUid);
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureDisabledProcStateCachedToUncached_fullWakeLockEnabled() {
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+        setCachedUidProcState(wakeLock.mOwnerUid);
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureEnabledProcStateCachedToUncached_screenBrightWakeLockEnabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+        setCachedUidProcState(wakeLock.mOwnerUid);
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureDisabledProcStateCachedToUncached_screenBrightWakeLockEnabled() {
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenBrightWakeLock",
+                PowerManager.SCREEN_BRIGHT_WAKE_LOCK);
+        setCachedUidProcState(wakeLock.mOwnerUid);
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureEnabledProcStateCachedToUncached_screenDimWakeLockEnabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+                PowerManager.SCREEN_DIM_WAKE_LOCK);
+        setCachedUidProcState(wakeLock.mOwnerUid);
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureDisabledProcStateCachedToUncached_screenDimWakeLockEnabled() {
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        createService();
+        startSystem();
+        WakeLock wakeLock = acquireWakeLock("screenDimWakeLock",
+                PowerManager.SCREEN_DIM_WAKE_LOCK);
+        setCachedUidProcState(wakeLock.mOwnerUid);
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    @Test
+    public void testFeatureDynamicallyDisabledProcStateUncachedToCached_fullWakeLockEnabled() {
+        doReturn(true).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        ArgumentCaptor<DeviceConfig.OnPropertiesChangedListener> listenerCaptor =
+                ArgumentCaptor.forClass(DeviceConfig.OnPropertiesChangedListener.class);
+        createService();
+        startSystem();
+        verify(mDeviceParameterProvider, times(1))
+                .addOnPropertiesChangedListener(any(), listenerCaptor.capture());
+        WakeLock wakeLock = acquireWakeLock("fullWakeLock", PowerManager.FULL_WAKE_LOCK);
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        // dynamically disable the feature
+        doReturn(false).when(mDeviceParameterProvider)
+                .isDisableScreenWakeLocksWhileCachedFeatureEnabled();
+        listenerCaptor.getValue().onPropertiesChanged(
+                new DeviceConfig.Properties("ignored_namespace", null));
+
+        setUncachedUidProcState(wakeLock.mOwnerUid);
+        assertThat(wakeLock.mDisabled).isFalse();
+    }
+
+    private void setCachedUidProcState(int uid) {
+        mService.updateUidProcStateInternal(uid, PROCESS_STATE_TOP_SLEEPING);
+    }
+
+    private void setUncachedUidProcState(int uid) {
+        mService.updateUidProcStateInternal(uid, PROCESS_STATE_RECEIVER);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/ShutdownCheckPointsTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java b/services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/ShutdownThreadTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/ShutdownThreadTest.java
diff --git a/services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java b/services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/power/WakeLockLogTest.java
rename to services/tests/powerservicetests/src/com/android/server/power/WakeLockLogTest.java
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 32d0c98..989aee0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -33,6 +33,7 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
@@ -42,6 +43,8 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
 import android.os.Handler;
 import android.os.Message;
 import android.os.UserHandle;
@@ -67,11 +70,13 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -123,9 +128,10 @@
     public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
     public static final int STATE_PANNING = 9;
     public static final int STATE_SCALING_AND_PANNING = 10;
+    public static final int STATE_SINGLE_PANNING = 11;
 
     public static final int FIRST_STATE = STATE_IDLE;
-    public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
+    public static final int LAST_STATE = STATE_SINGLE_PANNING;
 
     // Co-prime x and y, to potentially catch x-y-swapped errors
     public static final float DEFAULT_X = 301;
@@ -155,6 +161,10 @@
 
     private float mOriginalMagnificationPersistedScale;
 
+    static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
+
+    static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -182,11 +192,19 @@
                 new MagnificationScaleProvider(mContext),
                 () -> null,
                 ConcurrentUtils.DIRECT_EXECUTOR) {
-            @Override
-            public boolean magnificationRegionContains(int displayId, float x, float y) {
-                return true;
-            }
+                @Override
+                public boolean magnificationRegionContains(int displayId, float x, float y) {
+                    return true;
+                }
         };
+
+        doAnswer((Answer<Void>) invocationOnMock -> {
+            Object[] args = invocationOnMock.getArguments();
+            Region regionArg = (Region) args[1];
+            regionArg.set(new Rect(INITIAL_MAGNIFICATION_BOUNDS));
+            return null;
+        }).when(mockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
+
         mFullScreenMagnificationController.register(DISPLAY_0);
         mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
         mClock = new OffsettableClock.Stopped();
@@ -214,6 +232,7 @@
                 mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
                 detectTripleTap, detectShortcutTrigger,
                 mWindowMagnificationPromptController, DISPLAY_0);
+        h.setSinglePanningEnabled(true);
         mHandler = new TestHandler(h.mDetectingState, mClock) {
             @Override
             protected String messageToString(Message m) {
@@ -239,6 +258,7 @@
      * {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE})
      */
     @Test
+    @Ignore("b/291925580")
     public void testEachState_isReachableAndRecoverable() {
         forEachState(state -> {
             goFromStateIdleTo(state);
@@ -526,6 +546,75 @@
     }
 
     @Test
+    public void testActionUpNotAtEdge_singlePanningState_detectingState() {
+        goFromStateIdleTo(STATE_SINGLE_PANNING);
+
+        send(upEvent());
+
+        check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE);
+        assertTrue(isZoomed());
+    }
+
+    @Test
+    public void testScroll_SinglePanningDisabled_delegatingState() {
+        mMgh.setSinglePanningEnabled(false);
+
+        goFromStateIdleTo(STATE_ACTIVATED);
+        allowEventDelegation();
+        swipeAndHold();
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+    }
+
+    @Test
+    public void testScroll_zoomedStateAndAtEdge_delegatingState() {
+        goFromStateIdleTo(STATE_ACTIVATED);
+        mFullScreenMagnificationController.setCenter(
+                DISPLAY_0,
+                INITIAL_MAGNIFICATION_BOUNDS.left,
+                INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+                false,
+                1);
+        final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+        PointF initCoords =
+                new PointF(
+                        mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+                        mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+        PointF endCoords = new PointF(initCoords.x, initCoords.y);
+        endCoords.offset(swipeMinDistance, 0);
+        allowEventDelegation();
+
+        swipeAndHold(initCoords, endCoords);
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+        assertTrue(isZoomed());
+    }
+
+    @Test
+    public void testScroll_singlePanningAndAtEdge_delegatingState() {
+        goFromStateIdleTo(STATE_SINGLE_PANNING);
+        mFullScreenMagnificationController.setCenter(
+                DISPLAY_0,
+                INITIAL_MAGNIFICATION_BOUNDS.left,
+                INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+                false,
+                1);
+        final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+        PointF initCoords =
+                new PointF(
+                        mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+                        mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+        PointF endCoords = new PointF(initCoords.x, initCoords.y);
+        endCoords.offset(swipeMinDistance, 0);
+        allowEventDelegation();
+
+        swipeAndHold(initCoords, endCoords);
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+        assertTrue(isZoomed());
+    }
+
+    @Test
     public void testShortcutTriggered_invokeShowWindowPromptAction() {
         goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
 
@@ -740,6 +829,10 @@
                         state);
                 check(mMgh.mPanningScalingState.mScaling, state);
             } break;
+            case STATE_SINGLE_PANNING: {
+                check(isZoomed(), state);
+                check(mMgh.mCurrentState == mMgh.mSinglePanningState, state);
+            } break;
             default: throw new IllegalArgumentException("Illegal state: " + state);
         }
     }
@@ -803,6 +896,10 @@
                     send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
                     send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5));
                 } break;
+                case STATE_SINGLE_PANNING: {
+                    goFromStateIdleTo(STATE_ACTIVATED);
+                    swipeAndHold();
+                } break;
                 default:
                     throw new IllegalArgumentException("Illegal state: " + state);
             }
@@ -859,6 +956,10 @@
             case STATE_SCALING_AND_PANNING: {
                 returnToNormalFrom(STATE_PANNING);
             } break;
+            case STATE_SINGLE_PANNING: {
+                send(upEvent());
+                returnToNormalFrom(STATE_ACTIVATED);
+            } break;
             default: throw new IllegalArgumentException("Illegal state: " + state);
         }
     }
@@ -906,6 +1007,11 @@
         send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
     }
 
+    private void swipeAndHold(PointF start, PointF end) {
+        send(downEvent(start.x, start.y));
+        send(moveEvent(end.x, end.y));
+    }
+
     private void longTap() {
         send(downEvent());
         fastForward(2000);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 27e6ef1..bbbab21 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -66,7 +66,6 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.Mockito;
@@ -308,7 +307,6 @@
                 MagnificationScaleProvider.MAX_SCALE);
     }
 
-    @Ignore("b/278816260: We could refer to b/182561174#comment4 for solution.")
     @Test
     public void logTrackingTypingFocus_processScroll_logDuration() {
         WindowMagnificationManager spyWindowMagnificationManager = spy(mWindowMagnificationManager);
diff --git a/services/tests/servicestests/src/com/android/server/appop/OWNERS b/services/tests/servicestests/src/com/android/server/appop/OWNERS
index 999ea0e..2fe78c5 100644
--- a/services/tests/servicestests/src/com/android/server/appop/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/appop/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
 include /core/java/android/permission/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
index e457119..e7777f7 100644
--- a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java
@@ -42,6 +42,7 @@
 
 import com.android.server.LocalServices;
 import com.android.server.contentprotection.ContentProtectionBlocklistManager;
+import com.android.server.contentprotection.ContentProtectionConsentManager;
 import com.android.server.contentprotection.RemoteContentProtectionService;
 import com.android.server.pm.UserManagerInternal;
 
@@ -91,6 +92,8 @@
 
     @Mock private RemoteContentProtectionService mMockRemoteContentProtectionService;
 
+    @Mock private ContentProtectionConsentManager mMockContentProtectionConsentManager;
+
     private boolean mDevCfgEnableContentProtectionReceiver;
 
     private int mContentProtectionBlocklistManagersCreated;
@@ -99,6 +102,8 @@
 
     private int mRemoteContentProtectionServicesCreated;
 
+    private int mContentProtectionConsentManagersCreated;
+
     private String mConfigDefaultContentProtectionService = COMPONENT_NAME.flattenToString();
 
     private boolean mContentProtectionServiceInfoConstructorShouldThrow;
@@ -114,43 +119,51 @@
     }
 
     @Test
-    public void constructor_contentProtection_flagDisabled_noBlocklistManager() {
+    public void constructor_contentProtection_flagDisabled_noManagers() {
         assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
+        assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         verifyZeroInteractions(mMockContentProtectionBlocklistManager);
+        verifyZeroInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
-    public void constructor_contentProtection_componentNameNull_noBlocklistManager() {
+    public void constructor_contentProtection_componentNameNull_noManagers() {
         mConfigDefaultContentProtectionService = null;
 
         mContentCaptureManagerService = new TestContentCaptureManagerService();
 
         assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
+        assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         verifyZeroInteractions(mMockContentProtectionBlocklistManager);
+        verifyZeroInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
-    public void constructor_contentProtection_componentNameBlank_noBlocklistManager() {
+    public void constructor_contentProtection_componentNameBlank_noManagers() {
         mConfigDefaultContentProtectionService = "   ";
 
         mContentCaptureManagerService = new TestContentCaptureManagerService();
 
         assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
+        assertThat(mContentProtectionConsentManagersCreated).isEqualTo(0);
         verifyZeroInteractions(mMockContentProtectionBlocklistManager);
+        verifyZeroInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
-    public void constructor_contentProtection_enabled_createsBlocklistManager() {
+    public void constructor_contentProtection_enabled_createsManagers() {
         mDevCfgEnableContentProtectionReceiver = true;
 
         mContentCaptureManagerService = new TestContentCaptureManagerService();
 
         assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(1);
+        assertThat(mContentProtectionConsentManagersCreated).isEqualTo(1);
         assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0);
         verify(mMockContentProtectionBlocklistManager).updateBlocklist(anyInt());
+        verifyZeroInteractions(mMockContentProtectionConsentManager);
     }
 
     @Test
@@ -175,11 +188,13 @@
                         USER_ID, PACKAGE_NAME);
 
         assertThat(actual).isNull();
-        verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME);
+        verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
+        verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
     }
 
     @Test
     public void getOptions_contentCaptureDisabled_contentProtectionEnabled() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
         when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -211,11 +226,13 @@
         assertThat(actual.contentProtectionOptions).isNotNull();
         assertThat(actual.contentProtectionOptions.enableReceiver).isFalse();
         assertThat(actual.whitelistedComponents).isNull();
-        verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME);
+        verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
+        verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
     }
 
     @Test
     public void getOptions_contentCaptureEnabled_contentProtectionEnabled() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
         when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -234,7 +251,22 @@
     }
 
     @Test
+    public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionNotGranted() {
+        mDevCfgEnableContentProtectionReceiver = true;
+        mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+        boolean actual =
+                mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+                        USER_ID, PACKAGE_NAME);
+
+        assertThat(actual).isFalse();
+        verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
+        verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+    }
+
+    @Test
     public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionDisabled() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
 
@@ -248,6 +280,7 @@
 
     @Test
     public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionEnabled() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
         when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -271,11 +304,27 @@
                         USER_ID, PACKAGE_NAME);
 
         assertThat(actual).isTrue();
+        verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
+        verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
+    }
+
+    @Test
+    public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionNotGranted() {
+        mDevCfgEnableContentProtectionReceiver = true;
+        mContentCaptureManagerService = new TestContentCaptureManagerService();
+
+        boolean actual =
+                mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
+                        USER_ID, COMPONENT_NAME);
+
+        assertThat(actual).isFalse();
+        verify(mMockContentProtectionConsentManager).isConsentGranted(USER_ID);
         verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
     }
 
     @Test
     public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionDisabled() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
 
@@ -289,6 +338,7 @@
 
     @Test
     public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionEnabled() {
+        when(mMockContentProtectionConsentManager.isConsentGranted(USER_ID)).thenReturn(true);
         when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true);
         mDevCfgEnableContentProtectionReceiver = true;
         mContentCaptureManagerService = new TestContentCaptureManagerService();
@@ -312,16 +362,18 @@
                         USER_ID, COMPONENT_NAME);
 
         assertThat(actual).isTrue();
+        verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
         verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
     }
 
     @Test
-    public void isContentProtectionReceiverEnabled_withoutBlocklistManager() {
+    public void isContentProtectionReceiverEnabled_withoutManagers() {
         boolean actual =
                 mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted(
                         USER_ID, PACKAGE_NAME);
 
         assertThat(actual).isFalse();
+        verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
         verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
     }
 
@@ -336,6 +388,7 @@
                         USER_ID, PACKAGE_NAME);
 
         assertThat(actual).isFalse();
+        verify(mMockContentProtectionConsentManager, never()).isConsentGranted(anyInt());
         verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString());
     }
 
@@ -423,5 +476,11 @@
             mRemoteContentProtectionServicesCreated++;
             return mMockRemoteContentProtectionService;
         }
+
+        @Override
+        protected ContentProtectionConsentManager createContentProtectionConsentManager() {
+            mContentProtectionConsentManagersCreated++;
+            return mMockContentProtectionConsentManager;
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..0ffa891
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.contentcapture"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
new file mode 100644
index 0000000..0e80bfd
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionConsentManagerTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.contentprotection;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.TestableContentResolver;
+import android.testing.TestableContext;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/**
+ * Test for {@link ContentProtectionConsentManager}.
+ *
+ * <p>Run with: {@code atest
+ * FrameworksServicesTests:com.android.server.contentprotection.ContentProtectionConsentManagerTest}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ContentProtectionConsentManagerTest {
+
+    private static final String KEY_PACKAGE_VERIFIER_USER_CONSENT = "package_verifier_user_consent";
+
+    private static final Uri URI_PACKAGE_VERIFIER_USER_CONSENT =
+            Settings.Global.getUriFor(KEY_PACKAGE_VERIFIER_USER_CONSENT);
+
+    private static final int VALUE_TRUE = 1;
+
+    private static final int VALUE_FALSE = -1;
+
+    private static final int VALUE_DEFAULT = 0;
+
+    private static final int TEST_USER_ID = 1234;
+
+    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Rule
+    public final TestableContext mTestableContext =
+            new TestableContext(ApplicationProvider.getApplicationContext());
+
+    private final TestableContentResolver mTestableContentResolver =
+            mTestableContext.getContentResolver();
+
+    @Mock private ContentResolver mMockContentResolver;
+
+    @Mock private DevicePolicyManagerInternal mMockDevicePolicyManagerInternal;
+
+    @Test
+    public void constructor_registersContentObserver() {
+        ContentProtectionConsentManager manager =
+                createContentProtectionConsentManager(mMockContentResolver);
+
+        assertThat(manager.mContentObserver).isNotNull();
+        verify(mMockContentResolver)
+                .registerContentObserver(
+                        URI_PACKAGE_VERIFIER_USER_CONSENT,
+                        /* notifyForDescendants= */ false,
+                        manager.mContentObserver,
+                        UserHandle.USER_ALL);
+    }
+
+    @Test
+    public void isConsentGranted_packageVerifierNotGranted() {
+        ContentProtectionConsentManager manager =
+                createContentProtectionConsentManager(VALUE_FALSE);
+
+        boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+        assertThat(actual).isFalse();
+        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+    }
+
+    @Test
+    public void isConsentGranted_packageVerifierGranted_userNotManaged() {
+        ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE);
+
+        boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+        assertThat(actual).isTrue();
+        verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+    }
+
+    @Test
+    public void isConsentGranted_packageVerifierGranted_userManaged() {
+        when(mMockDevicePolicyManagerInternal.isUserOrganizationManaged(TEST_USER_ID))
+                .thenReturn(true);
+        ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE);
+
+        boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+        assertThat(actual).isFalse();
+    }
+
+    @Test
+    public void isConsentGranted_packageVerifierDefault() {
+        ContentProtectionConsentManager manager =
+                createContentProtectionConsentManager(VALUE_DEFAULT);
+
+        boolean actual = manager.isConsentGranted(TEST_USER_ID);
+
+        assertThat(actual).isFalse();
+        verifyZeroInteractions(mMockDevicePolicyManagerInternal);
+    }
+
+    @Test
+    public void contentObserver() throws Exception {
+        ContentProtectionConsentManager manager = createContentProtectionConsentManager(VALUE_TRUE);
+        boolean firstActual = manager.isConsentGranted(TEST_USER_ID);
+
+        Settings.Global.putInt(
+                mTestableContentResolver, KEY_PACKAGE_VERIFIER_USER_CONSENT, VALUE_FALSE);
+        // Observer has to be called manually, mTestableContentResolver is not propagating
+        manager.mContentObserver.onChange(
+                /* selfChange= */ false, URI_PACKAGE_VERIFIER_USER_CONSENT, TEST_USER_ID);
+        boolean secondActual = manager.isConsentGranted(TEST_USER_ID);
+
+        assertThat(firstActual).isTrue();
+        assertThat(secondActual).isFalse();
+        verify(mMockDevicePolicyManagerInternal).isUserOrganizationManaged(TEST_USER_ID);
+    }
+
+    private ContentProtectionConsentManager createContentProtectionConsentManager(
+            ContentResolver contentResolver) {
+        return new ContentProtectionConsentManager(
+                new Handler(Looper.getMainLooper()),
+                contentResolver,
+                mMockDevicePolicyManagerInternal);
+    }
+
+    private ContentProtectionConsentManager createContentProtectionConsentManager(
+            int valuePackageVerifierUserConsent) {
+        Settings.Global.putInt(
+                mTestableContentResolver,
+                KEY_PACKAGE_VERIFIER_USER_CONSENT,
+                valuePackageVerifierUserConsent);
+        return createContentProtectionConsentManager(mTestableContentResolver);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..419508c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.contentprotection"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
index d5ad815..b5bf1ea 100644
--- a/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/dreams/DreamControllerTest.java
@@ -16,7 +16,11 @@
 
 package com.android.server.dreams;
 
+import static android.os.PowerManager.USER_ACTIVITY_EVENT_OTHER;
+import static android.os.PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS;
+
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
@@ -32,7 +36,9 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IPowerManager;
 import android.os.IRemoteCallback;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.service.dreams.IDreamService;
@@ -58,6 +64,8 @@
 
     @Mock
     private ActivityTaskManager mActivityTaskManager;
+    @Mock
+    private IPowerManager mPowerManager;
 
     @Mock
     private IBinder mIBinder;
@@ -67,6 +75,8 @@
     @Captor
     private ArgumentCaptor<ServiceConnection> mServiceConnectionACaptor;
     @Captor
+    private ArgumentCaptor<IBinder.DeathRecipient> mDeathRecipientCaptor;
+    @Captor
     private ArgumentCaptor<IRemoteCallback> mRemoteCallbackCaptor;
 
     private final TestLooper mLooper = new TestLooper();
@@ -90,6 +100,12 @@
         when(mContext.getSystemServiceName(ActivityTaskManager.class))
                 .thenReturn(Context.ACTIVITY_TASK_SERVICE);
 
+        final PowerManager powerManager = new PowerManager(mContext, mPowerManager, null, null);
+        when(mContext.getSystemService(Context.POWER_SERVICE))
+                .thenReturn(powerManager);
+        when(mContext.getSystemServiceName(PowerManager.class))
+                .thenReturn(Context.POWER_SERVICE);
+
         mToken = new Binder();
         mDreamName = ComponentName.unflattenFromString("dream");
         mOverlayName = ComponentName.unflattenFromString("dream_overlay");
@@ -209,9 +225,51 @@
         verify(mIDreamService).detach();
     }
 
+    @Test
+    public void serviceDisconnect_resetsScreenTimeout() throws RemoteException {
+        // Start dream.
+        mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+        ServiceConnection serviceConnection = captureServiceConnection();
+        serviceConnection.onServiceConnected(mDreamName, mIBinder);
+        mLooper.dispatchAll();
+
+        // Dream disconnects unexpectedly.
+        serviceConnection.onServiceDisconnected(mDreamName);
+        mLooper.dispatchAll();
+
+        // Power manager receives user activity signal.
+        verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
+                eq(USER_ACTIVITY_EVENT_OTHER),
+                eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
+    }
+
+    @Test
+    public void binderDied_resetsScreenTimeout() throws RemoteException {
+        // Start dream.
+        mDreamController.startDream(mToken, mDreamName, false /*isPreview*/, false /*doze*/,
+                0 /*userId*/, null /*wakeLock*/, mOverlayName, "test" /*reason*/);
+        captureServiceConnection().onServiceConnected(mDreamName, mIBinder);
+        mLooper.dispatchAll();
+
+        // Dream binder dies.
+        captureDeathRecipient().binderDied();
+        mLooper.dispatchAll();
+
+        // Power manager receives user activity signal.
+        verify(mPowerManager).userActivity(/*displayId=*/ anyInt(), /*time=*/ anyLong(),
+                eq(USER_ACTIVITY_EVENT_OTHER),
+                eq(USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS));
+    }
+
     private ServiceConnection captureServiceConnection() {
         verify(mContext).bindServiceAsUser(any(), mServiceConnectionACaptor.capture(), anyInt(),
                 any());
         return mServiceConnectionACaptor.getValue();
     }
+
+    private IBinder.DeathRecipient captureDeathRecipient() throws RemoteException {
+        verify(mIBinder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt());
+        return mDeathRecipientCaptor.getValue();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a109d5c..0292bca 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -81,7 +81,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.FSI_FORCE_DEMOTE;
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI;
 import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION;
@@ -406,7 +405,6 @@
     UriGrantsManagerInternal mUgmInternal;
     @Mock
     AppOpsManager mAppOpsManager;
-    private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener;
     @Mock
     private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
             mNotificationAssistantAccessGrantedCallback;
@@ -606,12 +604,6 @@
         tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
                 SEARCH_SELECTOR_PKG);
 
-        doAnswer(invocation -> {
-            mOnPermissionChangeListener = invocation.getArgument(2);
-            return null;
-        }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(),
-                any());
-
         mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
         mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
                 mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -3224,6 +3216,48 @@
     }
 
     @Test
+    public void testUpdateAppNotifyCreatorBlock() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+        Thread.sleep(500);
+        waitForIdle();
+
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+        assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+                captor.getValue().getAction());
+        assertEquals(PKG, captor.getValue().getPackage());
+        assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+    }
+
+    @Test
+    public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+        mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
+        verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+    }
+
+    @Test
+    public void testUpdateAppNotifyCreatorUnblock() throws Exception {
+        when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+        mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
+        Thread.sleep(500);
+        waitForIdle();
+
+        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+        assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+                captor.getValue().getAction());
+        assertEquals(PKG, captor.getValue().getPackage());
+        assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+    }
+
+    @Test
     public void testUpdateChannelNotifyCreatorBlock() throws Exception {
         mService.setPreferencesHelper(mPreferencesHelper);
         when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -11169,8 +11203,6 @@
                 ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
         // Given: a notification from an app on the system partition has the flag
         // FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -11186,8 +11218,6 @@
     public void fixMediaNotification_withOnGoingFlag_shouldBeNonDismissible()
             throws Exception {
         // Given: a media notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .setStyle(new Notification.MediaStyle()
@@ -11216,8 +11246,6 @@
                 ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED);
         // Given: a notification from an app on the system partition has the flag
         // FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -11233,9 +11261,6 @@
     public void fixCallNotification_withOnGoingFlag_shouldNotBeNonDismissible()
             throws Exception {
         // Given: a call notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
-
         Person person = new Person.Builder()
                 .setName("caller")
                 .build();
@@ -11256,8 +11281,6 @@
     @Test
     public void fixNonExemptNotification_withOnGoingFlag_shouldBeDismissible() throws Exception {
         // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
                 .build();
@@ -11274,8 +11297,6 @@
             throws Exception {
         // Given: a non-exempt notification has the flag FLAG_NO_DISMISS set (even though this is
         // not allowed)
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .build();
         n.flags |= Notification.FLAG_NO_DISMISS;
@@ -11290,8 +11311,6 @@
     @Test
     public void fixMediaNotification_withoutOnGoingFlag_shouldBeDismissible() throws Exception {
         // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .setStyle(new Notification.MediaStyle()
@@ -11310,8 +11329,6 @@
             throws Exception {
         // Given: a media notification doesn't have the flag FLAG_ONGOING_EVENT set,
         // but has the flag FLAG_NO_DISMISS set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .setStyle(new Notification.MediaStyle()
@@ -11330,8 +11347,6 @@
     public void fixNonExempt_Notification_withoutOnGoingFlag_shouldBeDismissible()
             throws Exception {
         // Given: a non-exempt notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(false)
                 .build();
@@ -11348,8 +11363,6 @@
             throws Exception {
         when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         setDpmAppOppsExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
@@ -11376,8 +11389,6 @@
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
                 PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         setDpmAppOppsExemptFromDismissal(true);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
@@ -11397,8 +11408,6 @@
                 AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
                 PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
         // Given: a notification has the flag FLAG_ONGOING_EVENT set
-        // feature flag: ALLOW_DISMISS_ONGOING is on
-        mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
         setDpmAppOppsExemptFromDismissal(false);
         Notification n = new Notification.Builder(mContext, "test")
                 .setOngoing(true)
@@ -12139,134 +12148,6 @@
                 any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
     }
 
-    @Test
-    public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage()
-            throws RemoteException {
-        // Have preexisting posted notifications from revoked package and other packages.
-        mService.addNotification(new NotificationRecord(mContext,
-                generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel));
-        mService.addNotification(new NotificationRecord(mContext,
-                generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
-        // Have preexisting enqueued notifications from revoked package and other packages.
-        mService.addEnqueuedNotification(new NotificationRecord(mContext,
-                generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel));
-        mService.addEnqueuedNotification(new NotificationRecord(mContext,
-                generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
-        assertThat(mService.mNotificationList).hasSize(2);
-        assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
-        when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001);
-        when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
-        mOnPermissionChangeListener.onOpChanged(
-                AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0);
-        waitForIdle();
-
-        assertThat(mService.mNotificationList).hasSize(1);
-        assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other");
-        assertThat(mService.mEnqueuedNotifications).hasSize(1);
-        assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo(
-                "other");
-    }
-
-    @Test
-    public void onOpChanged_permissionStillGranted_notificationsAreNotAffected()
-            throws RemoteException {
-        // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission
-        // being now granted, AND having previously posted notifications from said package) should
-        // never happen (if we trust the broadcasts are correct). So this test is for a what-if
-        // scenario, to verify we still handle it reasonably.
-
-        // Have preexisting posted notifications from specific package and other packages.
-        mService.addNotification(new NotificationRecord(mContext,
-                generateSbn("granted", 1001, 1, 0), mTestNotificationChannel));
-        mService.addNotification(new NotificationRecord(mContext,
-                generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
-        // Have preexisting enqueued notifications from specific package and other packages.
-        mService.addEnqueuedNotification(new NotificationRecord(mContext,
-                generateSbn("granted", 1001, 3, 0), mTestNotificationChannel));
-        mService.addEnqueuedNotification(new NotificationRecord(mContext,
-                generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
-        assertThat(mService.mNotificationList).hasSize(2);
-        assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
-        when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001);
-        when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
-        mOnPermissionChangeListener.onOpChanged(
-                AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0);
-        waitForIdle();
-
-        assertThat(mService.mNotificationList).hasSize(2);
-        assertThat(mService.mEnqueuedNotifications).hasSize(2);
-    }
-
-    @Test
-    public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception {
-        when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
-        when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
-        mOnPermissionChangeListener.onOpChanged(
-                AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
-        waitForIdle();
-        mTestableLooper.moveTimeForward(500);
-        waitForIdle();
-
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
-        assertThat(captor.getValue().getAction()).isEqualTo(
-                NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
-        assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
-        assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse();
-    }
-
-    @Test
-    public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception {
-        when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
-        when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
-        mOnPermissionChangeListener.onOpChanged(
-                AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
-        waitForIdle();
-        mTestableLooper.moveTimeForward(500);
-        waitForIdle();
-
-        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
-        assertThat(captor.getValue().getAction()).isEqualTo(
-                NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
-        assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
-        assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue();
-    }
-
-    @Test
-    public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception {
-        mService.addNotification(new NotificationRecord(mContext,
-                generateSbn("package", 1001, 1, 0), mTestNotificationChannel));
-        assertThat(mService.mNotificationList).hasSize(1);
-        when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001);
-        when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn(
-                true);
-
-        // Start with granted permission and simulate effect of revoking it.
-        when(mPermissionHelper.hasPermission(1001)).thenReturn(true);
-        doAnswer(invocation -> {
-            when(mPermissionHelper.hasPermission(1001)).thenReturn(false);
-            mOnPermissionChangeListener.onOpChanged(
-                    AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0);
-            return null;
-        }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true);
-
-        mBinderService.setNotificationsEnabledForPackage("package", 1001, false);
-        waitForIdle();
-
-        assertThat(mService.mNotificationList).hasSize(0);
-
-        mTestableLooper.moveTimeForward(500);
-        waitForIdle();
-        verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null));
-    }
-
     private static <T extends Parcelable> T parcelAndUnparcel(T source,
             Parcelable.Creator<T> creator) {
         Parcel parcel = Parcel.obtain();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index dedb8f1..3ee75de 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -771,7 +771,7 @@
         mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.evaluateZenMode("test", true);
+            mZenModeHelper.evaluateZenModeLocked("test", true);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -798,7 +798,7 @@
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.evaluateZenMode("test", true);
+            mZenModeHelper.evaluateZenModeLocked("test", true);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -825,7 +825,7 @@
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
         for (int i = 0; i < 3; i++) {
             // if zen doesn't change, zen should not reapply itself to the ringer
-            mZenModeHelper.evaluateZenMode("test", true);
+            mZenModeHelper.evaluateZenModeLocked("test", true);
         }
         verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
                 mZenModeHelper.TAG);
@@ -2269,7 +2269,7 @@
         // Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
         // given that we don't have any zen rules active.
         mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
-        mZenModeHelper.evaluateZenMode("test", true);
+        mZenModeHelper.evaluateZenModeLocked("test", true);
 
         // Check that the change actually took: zen mode should be off now
         assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
diff --git a/services/tests/vibrator/AndroidManifest.xml b/services/tests/vibrator/AndroidManifest.xml
index 2a15c15..a14ea55 100644
--- a/services/tests/vibrator/AndroidManifest.xml
+++ b/services/tests/vibrator/AndroidManifest.xml
@@ -31,8 +31,7 @@
     <!-- Required to set always-on vibrations -->
     <uses-permission android:name="android.permission.VIBRATE_ALWAYS_ON" />
 
-    <application android:debuggable="true"
-        android:testOnly="true">
+    <application android:debuggable="true">
         <uses-library android:name="android.test.mock" android:required="true" />
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/services/tests/vibrator/TEST_MAPPING b/services/tests/vibrator/TEST_MAPPING
index f0a7e47..22b72fa 100644
--- a/services/tests/vibrator/TEST_MAPPING
+++ b/services/tests/vibrator/TEST_MAPPING
@@ -1,21 +1,7 @@
 {
-  "presubmit": [
+  "imports": [
     {
-      "name": "FrameworksVibratorServicesTests",
-      "options": [
-        {"exclude-annotation": "android.platform.test.annotations.LargeTest"},
-        {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
-        {"exclude-annotation": "androidx.test.filters.FlakyTest"},
-        {"exclude-annotation": "org.junit.Ignore"}
-      ]
-    }
-  ],
-  "postsubmit": [
-    {
-      "name": "FrameworksVibratorServicesTests",
-      "options": [
-        {"exclude-annotation": "org.junit.Ignore"}
-      ]
+      "path": "frameworks/base/services/core/java/com/android/server/vibrator"
     }
   ]
 }
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
new file mode 100644
index 0000000..a81898d
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/HapticFeedbackCustomizationTest.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vibrator;
+
+
+import static android.os.VibrationEffect.Composition.PRIMITIVE_TICK;
+import static android.os.VibrationEffect.EFFECT_CLICK;
+
+import static com.android.server.vibrator.HapticFeedbackCustomization.CustomizationParserException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.VibrationEffect;
+import android.util.AtomicFile;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+public class HapticFeedbackCustomizationTest {
+    @Rule public MockitoRule rule = MockitoJUnit.rule();
+
+    // Pairs of valid vibration XML along with their equivalent VibrationEffect.
+    private static final String COMPOSITION_VIBRATION_XML = "<vibration>"
+            + "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
+            + "</vibration>";
+    private static final VibrationEffect COMPOSITION_VIBRATION =
+            VibrationEffect.startComposition().addPrimitive(PRIMITIVE_TICK, 0.2497f).compose();
+
+    private static final String PREDEFINED_VIBRATION_XML =
+            "<vibration><predefined-effect name=\"click\"/></vibration>";
+    private static final VibrationEffect PREDEFINED_VIBRATION =
+            VibrationEffect.createPredefined(EFFECT_CLICK);
+
+    @Mock private Resources mResourcesMock;
+
+    @Test
+    public void testParseCustomizations_noCustomization_success() throws Exception {
+        assertParseCustomizationsSucceeds(
+                /* xml= */ "<haptic-feedback-constants></haptic-feedback-constants>",
+                /* expectedCustomizations= */ new SparseArray<>());
+    }
+
+    @Test
+    public void testParseCustomizations_oneCustomization_success() throws Exception {
+        String xml = "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
+        expectedMapping.put(10, COMPOSITION_VIBRATION);
+
+        assertParseCustomizationsSucceeds(xml, expectedMapping);
+    }
+
+    @Test
+    public void testParseCustomizations_multipleCustomizations_success() throws Exception {
+        String xml = "<haptic-feedback-constants>"
+                + "<constant id=\"1\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"12\">"
+                + PREDEFINED_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"150\">"
+                + PREDEFINED_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>";
+        SparseArray<VibrationEffect> expectedMapping = new SparseArray<>();
+        expectedMapping.put(1, COMPOSITION_VIBRATION);
+        expectedMapping.put(12, PREDEFINED_VIBRATION);
+        expectedMapping.put(150, PREDEFINED_VIBRATION);
+
+        assertParseCustomizationsSucceeds(xml, expectedMapping);
+    }
+
+    @Test
+    public void testParseCustomizations_noCustomizationFile_returnsNull() throws Exception {
+        setCustomizationFilePath("");
+
+        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock)).isNull();
+
+        setCustomizationFilePath(null);
+
+        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock)).isNull();
+
+        setCustomizationFilePath("non_existent_file.xml");
+
+        assertThat(HapticFeedbackCustomization.loadVibrations(mResourcesMock)).isNull();
+    }
+
+    @Test
+    public void testParseCustomizations_disallowedVibrationForHapticFeedback_throwsException()
+            throws Exception {
+        // The XML content is good, but the serialized vibration is not supported for haptic
+        // feedback usage (i.e. repeating vibration).
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + "<vibration>"
+                + "<waveform-effect>"
+                + "<repeating>"
+                + "<waveform-entry durationMs=\"10\" amplitude=\"100\"/>"
+                + "</repeating>"
+                + "</waveform-effect>"
+                + "</vibration>"
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+    }
+
+    @Test
+    public void testParseCustomizations_emptyXml_throwsException() throws Exception {
+        assertParseCustomizationsFails("");
+    }
+
+    @Test
+    public void testParseCustomizations_noVibrationXml_throwsException() throws Exception {
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"1\">"
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+    }
+
+    @Test
+    public void testParseCustomizations_badEffectId_throwsException() throws Exception {
+        // Negative id
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"-10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+
+        // Non-numeral id
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"xyz\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+    }
+
+    @Test
+    public void testParseCustomizations_malformedXml_throwsException() throws Exception {
+        // No start "<constant>" tag
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+
+        // No end "<constant>" tag
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</haptic-feedback-constants>");
+
+        // No start "<haptic-feedback-constants>" tag
+        assertParseCustomizationsFails(
+                "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+
+        // No end "<haptic-feedback-constants>" tag
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>");
+    }
+
+    @Test
+    public void testParseCustomizations_badVibrationXml_throwsException() throws Exception {
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + "<bad-vibration></bad-vibration>"
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + "<vibration><predefined-effect name=\"bad-effect-name\"/></vibration>"
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+    }
+
+    @Test
+    public void testParseCustomizations_badConstantAttribute_throwsException() throws Exception {
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant iddddd=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\" unwanted-attr=\"1\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+    }
+
+    @Test
+    public void testParseCustomizations_duplicateEffects_throwsException() throws Exception {
+        assertParseCustomizationsFails(
+                "<haptic-feedback-constants>"
+                + "<constant id=\"10\">"
+                + COMPOSITION_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"10\">"
+                + PREDEFINED_VIBRATION_XML
+                + "</constant>"
+                + "<constant id=\"11\">"
+                + PREDEFINED_VIBRATION_XML
+                + "</constant>"
+                + "</haptic-feedback-constants>");
+    }
+
+    private void assertParseCustomizationsSucceeds(
+            String xml, SparseArray<VibrationEffect> expectedCustomizations) throws Exception {
+        setupCustomizationFile(xml);
+        assertThat(expectedCustomizations.contentEquals(
+                HapticFeedbackCustomization.loadVibrations(mResourcesMock))).isTrue();
+    }
+
+    private void assertParseCustomizationsFails(String xml) throws Exception {
+        setupCustomizationFile(xml);
+        assertThrows("Expected haptic feedback customization to fail for " + xml,
+                CustomizationParserException.class,
+                () ->  HapticFeedbackCustomization.loadVibrations(mResourcesMock));
+    }
+
+    private void assertParseCustomizationsFails() throws Exception {
+        assertThrows("Expected haptic feedback customization to fail",
+                CustomizationParserException.class,
+                () ->  HapticFeedbackCustomization.loadVibrations(mResourcesMock));
+    }
+
+    private void setupCustomizationFile(String xml) throws Exception {
+        File file = createFile(xml);
+        setCustomizationFilePath(file.getAbsolutePath());
+    }
+
+    private void setCustomizationFilePath(String path) {
+        when(mResourcesMock.getString(R.string.config_hapticFeedbackCustomizationFile))
+                .thenReturn(path);
+    }
+
+    private static File createFile(String contents) throws Exception {
+        File file = new File(InstrumentationRegistry.getContext().getCacheDir(), "test.xml");
+        file.createNewFile();
+
+        AtomicFile testAtomicXmlFile = new AtomicFile(file);
+        FileOutputStream fos = testAtomicXmlFile.startWrite();
+        fos.write(contents.getBytes());
+        testAtomicXmlFile.finishWrite(fos);
+
+        return file;
+    }
+}
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index a0e6cf5..c2812a1 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -52,6 +52,7 @@
         "service-permission.stubs.system_server",
         "androidx.test.runner",
         "androidx.test.rules",
+        "junit-params",
         "mockito-target-extended-minus-junit4",
         "platform-test-annotations",
         "servicestests-utils",
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 2015ae9..bf88ce4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -63,7 +63,7 @@
     final Context mContext = spy(getInstrumentation().getTargetContext());
 
     /** Modifier key to meta state */
-    private static final Map<Integer, Integer> MODIFIER;
+    protected static final Map<Integer, Integer> MODIFIER;
     static {
         final Map<Integer, Integer> map = new ArrayMap<>();
         map.put(KEYCODE_CTRL_LEFT, META_CTRL_LEFT_ON | META_CTRL_ON);
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
new file mode 100644
index 0000000..feca326
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutLoggingTests.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.policy;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.KeyEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+@Presubmit
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class ShortcutLoggingTests extends ShortcutKeyTestBase {
+
+    private static final int VENDOR_ID = 0x123;
+    private static final int PRODUCT_ID = 0x456;
+    private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
+    private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
+    private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
+    private static final int ALT_ON = MODIFIER.get(KeyEvent.KEYCODE_ALT_LEFT);
+    private static final int CTRL_KEY = KeyEvent.KEYCODE_CTRL_LEFT;
+    private static final int CTRL_ON = MODIFIER.get(KeyEvent.KEYCODE_CTRL_LEFT);
+    private static final int SHIFT_KEY = KeyEvent.KEYCODE_SHIFT_LEFT;
+    private static final int SHIFT_ON = MODIFIER.get(KeyEvent.KEYCODE_SHIFT_LEFT);
+
+    @Keep
+    private static Object[][] shortcutTestArguments() {
+        // testName, testKeys, expectedLogEvent, expectedKey, expectedModifierState
+        return new Object[][]{
+                {"Meta + H -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_H},
+                        KeyboardLogEvent.HOME, KeyEvent.KEYCODE_H, META_ON},
+                {"Meta + Enter -> Open Home", new int[]{META_KEY, KeyEvent.KEYCODE_ENTER},
+                        KeyboardLogEvent.HOME, KeyEvent.KEYCODE_ENTER, META_ON},
+                {"HOME key -> Open Home", new int[]{KeyEvent.KEYCODE_HOME}, KeyboardLogEvent.HOME,
+                        KeyEvent.KEYCODE_HOME, 0},
+                {"RECENT_APPS key -> Open Overview", new int[]{KeyEvent.KEYCODE_RECENT_APPS},
+                        KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_RECENT_APPS, 0},
+                {"Meta + Tab -> Open OVerview", new int[]{META_KEY, KeyEvent.KEYCODE_TAB},
+                        KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, META_ON},
+                {"Alt + Tab -> Open Overview", new int[]{ALT_KEY, KeyEvent.KEYCODE_TAB},
+                        KeyboardLogEvent.RECENT_APPS, KeyEvent.KEYCODE_TAB, ALT_ON},
+                {"BACK key -> Go back", new int[]{KeyEvent.KEYCODE_BACK}, KeyboardLogEvent.BACK,
+                        KeyEvent.KEYCODE_BACK, 0},
+                {"APP_SWITCH key -> Open App switcher", new int[]{KeyEvent.KEYCODE_APP_SWITCH},
+                        KeyboardLogEvent.APP_SWITCH, KeyEvent.KEYCODE_APP_SWITCH, 0},
+                {"ASSIST key -> Launch assistant", new int[]{KeyEvent.KEYCODE_ASSIST},
+                        KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_ASSIST, 0},
+                {"Meta + A -> Launch assistant", new int[]{META_KEY, KeyEvent.KEYCODE_A},
+                        KeyboardLogEvent.LAUNCH_ASSISTANT, KeyEvent.KEYCODE_A, META_ON},
+                {"VOICE_ASSIST key -> Launch Voice Assistant",
+                        new int[]{KeyEvent.KEYCODE_VOICE_ASSIST},
+                        KeyboardLogEvent.LAUNCH_VOICE_ASSISTANT, KeyEvent.KEYCODE_VOICE_ASSIST, 0},
+                {"Meta + I -> Launch System Settings", new int[]{META_KEY, KeyEvent.KEYCODE_I},
+                        KeyboardLogEvent.LAUNCH_SYSTEM_SETTINGS, KeyEvent.KEYCODE_I, META_ON},
+                {"Meta + N -> Toggle Notification panel", new int[]{META_KEY, KeyEvent.KEYCODE_N},
+                        KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_N, META_ON},
+                {"NOTIFICATION key -> Toggle Notification Panel",
+                        new int[]{KeyEvent.KEYCODE_NOTIFICATION},
+                        KeyboardLogEvent.TOGGLE_NOTIFICATION_PANEL, KeyEvent.KEYCODE_NOTIFICATION,
+                        0},
+                {"Meta + T -> Toggle Taskbar", new int[]{META_KEY, KeyEvent.KEYCODE_T},
+                        KeyboardLogEvent.TOGGLE_TASKBAR, KeyEvent.KEYCODE_T, META_ON},
+                {"Meta + Ctrl + S -> Take Screenshot",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_S},
+                        KeyboardLogEvent.TAKE_SCREENSHOT, KeyEvent.KEYCODE_S, META_ON | CTRL_ON},
+                {"Meta + / -> Open Shortcut Helper", new int[]{META_KEY, KeyEvent.KEYCODE_SLASH},
+                        KeyboardLogEvent.OPEN_SHORTCUT_HELPER, KeyEvent.KEYCODE_SLASH, META_ON},
+                {"BRIGHTNESS_UP key -> Increase Brightness",
+                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_UP}, KeyboardLogEvent.BRIGHTNESS_UP,
+                        KeyEvent.KEYCODE_BRIGHTNESS_UP, 0},
+                {"BRIGHTNESS_DOWN key -> Decrease Brightness",
+                        new int[]{KeyEvent.KEYCODE_BRIGHTNESS_DOWN},
+                        KeyboardLogEvent.BRIGHTNESS_DOWN, KeyEvent.KEYCODE_BRIGHTNESS_DOWN, 0},
+                {"KEYBOARD_BACKLIGHT_UP key -> Increase Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP},
+                        KeyboardLogEvent.KEYBOARD_BACKLIGHT_UP,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP, 0},
+                {"KEYBOARD_BACKLIGHT_DOWN key -> Decrease Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN},
+                        KeyboardLogEvent.KEYBOARD_BACKLIGHT_DOWN,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN, 0},
+                {"KEYBOARD_BACKLIGHT_TOGGLE key -> Toggle Keyboard Backlight",
+                        new int[]{KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE},
+                        KeyboardLogEvent.KEYBOARD_BACKLIGHT_TOGGLE,
+                        KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE, 0},
+                {"VOLUME_UP key -> Increase Volume", new int[]{KeyEvent.KEYCODE_VOLUME_UP},
+                        KeyboardLogEvent.VOLUME_UP, KeyEvent.KEYCODE_VOLUME_UP, 0},
+                {"VOLUME_DOWN key -> Decrease Volume", new int[]{KeyEvent.KEYCODE_VOLUME_DOWN},
+                        KeyboardLogEvent.VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN, 0},
+                {"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
+                        KeyboardLogEvent.VOLUME_MUTE, KeyEvent.KEYCODE_VOLUME_MUTE, 0},
+                {"ALL_APPS key -> Open App Drawer", new int[]{KeyEvent.KEYCODE_ALL_APPS},
+                        KeyboardLogEvent.ALL_APPS, KeyEvent.KEYCODE_ALL_APPS, 0},
+                {"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
+                        KeyboardLogEvent.LAUNCH_SEARCH, KeyEvent.KEYCODE_SEARCH, 0},
+                {"LANGUAGE_SWITCH key -> Switch Keyboard Language",
+                        new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
+                        KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
+                {"Meta + Space -> Switch Keyboard Language",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_SPACE},
+                        KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE, META_ON},
+                {"Meta + Shift + Space -> Switch Keyboard Language",
+                        new int[]{META_KEY, SHIFT_KEY, KeyEvent.KEYCODE_SPACE},
+                        KeyboardLogEvent.LANGUAGE_SWITCH, KeyEvent.KEYCODE_SPACE,
+                        META_ON | SHIFT_ON},
+                {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
+                        KeyboardLogEvent.ACCESSIBILITY_ALL_APPS, META_KEY, META_ON},
+                {"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
+                        KeyboardLogEvent.TOGGLE_CAPS_LOCK, ALT_KEY, META_ON | ALT_ON},
+                {"Alt + Meta -> Toggle CapsLock", new int[]{ALT_KEY, META_KEY},
+                        KeyboardLogEvent.TOGGLE_CAPS_LOCK, META_KEY, META_ON | ALT_ON},
+                {"CAPS_LOCK key -> Toggle CapsLock", new int[]{KeyEvent.KEYCODE_CAPS_LOCK},
+                        KeyboardLogEvent.TOGGLE_CAPS_LOCK, KeyEvent.KEYCODE_CAPS_LOCK, 0},
+                {"MUTE key -> Mute System Microphone", new int[]{KeyEvent.KEYCODE_MUTE},
+                        KeyboardLogEvent.SYSTEM_MUTE, KeyEvent.KEYCODE_MUTE, 0},
+                {"Meta + Ctrl + DPAD_UP -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_UP},
+                        KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_UP,
+                        META_ON | CTRL_ON},
+                {"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
+                        KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_LEFT,
+                        META_ON | CTRL_ON},
+                {"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
+                        new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
+                        KeyboardLogEvent.SPLIT_SCREEN_NAVIGATION, KeyEvent.KEYCODE_DPAD_RIGHT,
+                        META_ON | CTRL_ON},
+                {"Shift + Menu -> Trigger Bug Report", new int[]{SHIFT_KEY, KeyEvent.KEYCODE_MENU},
+                        KeyboardLogEvent.TRIGGER_BUG_REPORT, KeyEvent.KEYCODE_MENU, SHIFT_ON},
+                {"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
+                        KeyboardLogEvent.LOCK_SCREEN, KeyEvent.KEYCODE_L, META_ON},
+                {"Meta + Ctrl + N -> Open Notes", new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_N},
+                        KeyboardLogEvent.OPEN_NOTES, KeyEvent.KEYCODE_N, META_ON | CTRL_ON},
+                {"POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_POWER},
+                        KeyboardLogEvent.TOGGLE_POWER, KeyEvent.KEYCODE_POWER, 0},
+                {"TV_POWER key -> Toggle Power", new int[]{KeyEvent.KEYCODE_TV_POWER},
+                        KeyboardLogEvent.TOGGLE_POWER, KeyEvent.KEYCODE_TV_POWER, 0},
+                {"SYSTEM_NAVIGATION_DOWN key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN},
+                        KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN,
+                        0},
+                {"SYSTEM_NAVIGATION_UP key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP},
+                        KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP,
+                        0},
+                {"SYSTEM_NAVIGATION_LEFT key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT},
+                        KeyboardLogEvent.SYSTEM_NAVIGATION, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT,
+                        0},
+                {"SYSTEM_NAVIGATION_RIGHT key -> System Navigation",
+                        new int[]{KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT},
+                        KeyboardLogEvent.SYSTEM_NAVIGATION,
+                        KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT, 0},
+                {"SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SLEEP},
+                        KeyboardLogEvent.SLEEP, KeyEvent.KEYCODE_SLEEP, 0},
+                {"SOFT_SLEEP key -> System Sleep", new int[]{KeyEvent.KEYCODE_SOFT_SLEEP},
+                        KeyboardLogEvent.SLEEP, KeyEvent.KEYCODE_SOFT_SLEEP, 0},
+                {"WAKEUP key -> System Wakeup", new int[]{KeyEvent.KEYCODE_WAKEUP},
+                        KeyboardLogEvent.WAKEUP, KeyEvent.KEYCODE_WAKEUP, 0},
+                {"MEDIA_PLAY key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PLAY},
+                        KeyboardLogEvent.MEDIA_KEY, KeyEvent.KEYCODE_MEDIA_PLAY, 0},
+                {"MEDIA_PAUSE key -> Media Control", new int[]{KeyEvent.KEYCODE_MEDIA_PAUSE},
+                        KeyboardLogEvent.MEDIA_KEY, KeyEvent.KEYCODE_MEDIA_PAUSE, 0},
+                {"MEDIA_PLAY_PAUSE key -> Media Control",
+                        new int[]{KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE}, KeyboardLogEvent.MEDIA_KEY,
+                        KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, 0},
+                {"Meta + B -> Launch Default Browser", new int[]{META_KEY, KeyEvent.KEYCODE_B},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_BROWSER, KeyEvent.KEYCODE_B, META_ON},
+                {"EXPLORER key -> Launch Default Browser", new int[]{KeyEvent.KEYCODE_EXPLORER},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_BROWSER, KeyEvent.KEYCODE_EXPLORER, 0},
+                {"Meta + C -> Launch Default Contacts", new int[]{META_KEY, KeyEvent.KEYCODE_C},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_CONTACTS, KeyEvent.KEYCODE_C, META_ON},
+                {"CONTACTS key -> Launch Default Contacts", new int[]{KeyEvent.KEYCODE_CONTACTS},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_CONTACTS, KeyEvent.KEYCODE_CONTACTS, 0},
+                {"Meta + E -> Launch Default Email", new int[]{META_KEY, KeyEvent.KEYCODE_E},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_EMAIL, KeyEvent.KEYCODE_E, META_ON},
+                {"ENVELOPE key -> Launch Default Email", new int[]{KeyEvent.KEYCODE_ENVELOPE},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_EMAIL, KeyEvent.KEYCODE_ENVELOPE, 0},
+                {"Meta + K -> Launch Default Calendar", new int[]{META_KEY, KeyEvent.KEYCODE_K},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_CALENDAR, KeyEvent.KEYCODE_K, META_ON},
+                {"CALENDAR key -> Launch Default Calendar", new int[]{KeyEvent.KEYCODE_CALENDAR},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_CALENDAR, KeyEvent.KEYCODE_CALENDAR, 0},
+                {"Meta + P -> Launch Default Music", new int[]{META_KEY, KeyEvent.KEYCODE_P},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_MUSIC, KeyEvent.KEYCODE_P, META_ON},
+                {"MUSIC key -> Launch Default Music", new int[]{KeyEvent.KEYCODE_MUSIC},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_MUSIC, KeyEvent.KEYCODE_MUSIC, 0},
+                {"Meta + U -> Launch Default Calculator", new int[]{META_KEY, KeyEvent.KEYCODE_U},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_CALCULATOR, KeyEvent.KEYCODE_U, META_ON},
+                {"CALCULATOR key -> Launch Default Calculator",
+                        new int[]{KeyEvent.KEYCODE_CALCULATOR},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_CALCULATOR, KeyEvent.KEYCODE_CALCULATOR, 0},
+                {"Meta + M -> Launch Default Maps", new int[]{META_KEY, KeyEvent.KEYCODE_M},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_MAPS, KeyEvent.KEYCODE_M, META_ON},
+                {"Meta + S -> Launch Default Messaging App",
+                        new int[]{META_KEY, KeyEvent.KEYCODE_S},
+                        KeyboardLogEvent.LAUNCH_DEFAULT_MESSAGING, KeyEvent.KEYCODE_S, META_ON}};
+    }
+
+    @Before
+    @Override
+    public void setUp() {
+        super.setUp();
+        mPhoneWindowManager.overrideKeyEventSource(VENDOR_ID, PRODUCT_ID);
+        mPhoneWindowManager.overrideLaunchHome();
+        mPhoneWindowManager.overrideSearchKeyBehavior(
+                PhoneWindowManager.SEARCH_BEHAVIOR_TARGET_ACTIVITY);
+        mPhoneWindowManager.overrideEnableBugReportTrigger(true);
+        mPhoneWindowManager.overrideStatusBarManagerInternal();
+        mPhoneWindowManager.overrideStartActivity();
+        mPhoneWindowManager.overrideUserSetupComplete();
+    }
+
+    @Test
+    @Parameters(method = "shortcutTestArguments")
+    public void testShortcuts(String testName, int[] testKeys, KeyboardLogEvent expectedLogEvent,
+            int expectedKey, int expectedModifierState) {
+        sendKeyCombination(testKeys, 0);
+        mPhoneWindowManager.assertShortcutLogged(VENDOR_ID, PRODUCT_ID, expectedLogEvent,
+                expectedKey, expectedModifierState, "Failed while executing " + testName);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 766a88f..1866767 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -26,6 +26,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.description;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
@@ -35,6 +36,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
 import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GO_TO_VOICE_ASSIST;
@@ -50,7 +52,6 @@
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mockingDetails;
 import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.withSettings;
 
 import android.app.ActivityManagerInternal;
@@ -60,8 +61,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.hardware.SensorPrivacyManager;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
+import android.hardware.input.InputManager;
 import android.media.AudioManagerInternal;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -75,14 +78,17 @@
 import android.telecom.TelecomManager;
 import android.util.FeatureFlagUtils;
 import android.view.Display;
+import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.autofill.AutofillManagerInternal;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.GestureLauncherService;
 import com.android.server.LocalServices;
 import com.android.server.input.InputManagerInternal;
+import com.android.server.input.KeyboardMetricsCollector.KeyboardLogEvent;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.policy.keyguard.KeyguardServiceDelegate;
@@ -102,6 +108,7 @@
 import org.mockito.MockSettings;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.quality.Strictness;
 
 import java.util.function.Supplier;
 
@@ -117,6 +124,8 @@
     @Mock private ActivityManagerInternal mActivityManagerInternal;
     @Mock private ActivityTaskManagerInternal mActivityTaskManagerInternal;
     @Mock private InputManagerInternal mInputManagerInternal;
+    @Mock private InputManager mInputManager;
+    @Mock private SensorPrivacyManager mSensorPrivacyManager;
     @Mock private DreamManagerInternal mDreamManagerInternal;
     @Mock private PowerManagerInternal mPowerManagerInternal;
     @Mock private DisplayManagerInternal mDisplayManagerInternal;
@@ -186,6 +195,8 @@
         // Return mocked services: LocalServices.getService
         mMockitoSession = mockitoSession()
                 .mockStatic(LocalServices.class, spyStubOnly)
+                .mockStatic(FrameworkStatsLog.class)
+                .strictness(Strictness.LENIENT)
                 .startMocking();
 
         doReturn(mWindowManagerInternal).when(
@@ -213,7 +224,10 @@
 
         doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
         doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
+        doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
         doReturn(mPackageManager).when(mContext).getPackageManager();
+        doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
+                eq(SensorPrivacyManager.class));
         doReturn(false).when(mPackageManager).hasSystemFeature(any());
         try {
             doThrow(new PackageManager.NameNotFoundException("test")).when(mPackageManager)
@@ -409,6 +423,31 @@
         doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing();
     }
 
+    void overrideKeyEventSource(int vendorId, int productId) {
+        InputDevice device = new InputDevice.Builder().setId(1).setVendorId(vendorId).setProductId(
+                productId).setSources(InputDevice.SOURCE_KEYBOARD).setKeyboardType(
+                InputDevice.KEYBOARD_TYPE_ALPHABETIC).build();
+        doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
+        doReturn(device).when(mInputManager).getInputDevice(anyInt());
+    }
+
+    void overrideSearchKeyBehavior(int behavior) {
+        mPhoneWindowManager.mSearchKeyBehavior = behavior;
+    }
+
+    void overrideEnableBugReportTrigger(boolean enable) {
+        mPhoneWindowManager.mEnableShiftMenuBugReports = enable;
+    }
+
+    void overrideStartActivity() {
+        doNothing().when(mContext).startActivityAsUser(any(), any());
+        doNothing().when(mContext).startActivityAsUser(any(), any(), any());
+    }
+
+    void overrideUserSetupComplete() {
+        doReturn(true).when(mPhoneWindowManager).isUserSetupComplete();
+    }
+
     /**
      * Below functions will check the policy behavior could be invoked.
      */
@@ -563,4 +602,12 @@
         verify(mContext, after(TEST_SINGLE_KEY_DELAY_MILLIS).never())
                 .startActivityAsUser(any(Intent.class), any(), any(UserHandle.class));
     }
+
+    void assertShortcutLogged(int vendorId, int productId, KeyboardLogEvent logEvent,
+            int expectedKey, int expectedModifierState, String errorMsg) {
+        waitForIdle();
+        verify(() -> FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED,
+                        vendorId, productId, logEvent.getIntValue(), new int[]{expectedKey},
+                        expectedModifierState), description(errorMsg));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 5c3102d..65e77dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -182,12 +182,12 @@
 
     @Test
     public void testLaunchState() {
-        final ToIntFunction<Boolean> launchTemplate = doRelaunch -> {
+        final ToIntFunction<Runnable> launchTemplate = action -> {
             clearInvocations(mLaunchObserver);
             onActivityLaunched(mTopActivity);
             notifyTransitionStarting(mTopActivity);
-            if (doRelaunch) {
-                mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity);
+            if (action != null) {
+                action.run();
             }
             final ActivityMetricsLogger.TransitionInfoSnapshot info =
                     notifyWindowsDrawn(mTopActivity);
@@ -199,21 +199,27 @@
         // Assume that the process is started (ActivityBuilder has mocked the returned value of
         // ATMS#getProcessController) but the activity has not attached process.
         mTopActivity.app = null;
-        assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+        assertWithMessage("Warm launch").that(launchTemplate.applyAsInt(null))
                 .isEqualTo(WaitResult.LAUNCH_STATE_WARM);
 
         mTopActivity.app = app;
         mNewActivityCreated = false;
-        assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+        assertWithMessage("Hot launch").that(launchTemplate.applyAsInt(null))
                 .isEqualTo(WaitResult.LAUNCH_STATE_HOT);
 
-        assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(true /* doRelaunch */))
+        assertWithMessage("Relaunch").that(launchTemplate.applyAsInt(
+                () -> mActivityMetricsLogger.notifyActivityRelaunched(mTopActivity)))
                 .isEqualTo(WaitResult.LAUNCH_STATE_RELAUNCH);
 
+        assertWithMessage("Cold launch by restart").that(launchTemplate.applyAsInt(
+                () -> mActivityMetricsLogger.notifyBindApplication(
+                        mTopActivity.info.applicationInfo)))
+                .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
+
         mTopActivity.app = null;
         mNewActivityCreated = true;
         doReturn(null).when(mAtm).getProcessController(app.mName, app.mUid);
-        assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(false /* doRelaunch */))
+        assertWithMessage("Cold launch").that(launchTemplate.applyAsInt(null))
                 .isEqualTo(WaitResult.LAUNCH_STATE_COLD);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index fe1ea0d..f6f3f03 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -22,11 +22,17 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -41,6 +47,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
+import android.util.Log;
 import android.util.Rational;
 import android.view.SurfaceControl;
 import android.window.TaskOrganizer;
@@ -48,7 +55,10 @@
 import androidx.test.filters.MediumTest;
 
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -166,6 +176,122 @@
         }
     }
 
+    /**
+     * Tests if any unknown key is being used in the ActivityOptions bundle. If so, please review
+     * if the newly added bundle should be protected with permissions to avoid malicious attacks.
+     *
+     * @see SafeActivityOptionsTest#test_getOptions
+     */
+    @Test
+    public void testActivityOptionsFromBundle() {
+        // Spy on a bundle that is generated from a basic ActivityOptions.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        Bundle bundle = options.toBundle();
+        spyOn(bundle);
+
+        // Create a new ActivityOptions from the bundle
+        new ActivityOptions(bundle);
+
+        // Verify the keys that are being used.
+        final ArgumentCaptor<String> stringCaptor =  ArgumentCaptor.forClass(String.class);
+        verify(bundle, atLeastOnce()).getString(stringCaptor.capture());
+        verify(bundle, atLeastOnce()).getBoolean(stringCaptor.capture());
+        verify(bundle, atLeastOnce()).getParcelable(stringCaptor.capture(), any());
+        verify(bundle, atLeastOnce()).getInt(stringCaptor.capture(), anyInt());
+        verify(bundle, atLeastOnce()).getBinder(stringCaptor.capture());
+        verify(bundle, atLeastOnce()).getBundle(stringCaptor.capture());
+        final List<String> keys = stringCaptor.getAllValues();
+        final List<String> unknownKeys = new ArrayList<>();
+        for (String key : keys) {
+            switch (key) {
+                case ActivityOptions.KEY_PACKAGE_NAME:
+                case ActivityOptions.KEY_LAUNCH_BOUNDS:
+                case ActivityOptions.KEY_ANIM_TYPE:
+                case ActivityOptions.KEY_ANIM_ENTER_RES_ID:
+                case ActivityOptions.KEY_ANIM_EXIT_RES_ID:
+                case ActivityOptions.KEY_ANIM_IN_PLACE_RES_ID:
+                case ActivityOptions.KEY_ANIM_BACKGROUND_COLOR:
+                case ActivityOptions.KEY_ANIM_THUMBNAIL:
+                case ActivityOptions.KEY_ANIM_START_X:
+                case ActivityOptions.KEY_ANIM_START_Y:
+                case ActivityOptions.KEY_ANIM_WIDTH:
+                case ActivityOptions.KEY_ANIM_HEIGHT:
+                case ActivityOptions.KEY_ANIM_START_LISTENER:
+                case ActivityOptions.KEY_SPLASH_SCREEN_THEME:
+                case ActivityOptions.KEY_LEGACY_PERMISSION_PROMPT_ELIGIBLE:
+                case ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN:
+                case ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN:
+                case ActivityOptions.KEY_TRANSIENT_LAUNCH:
+                case "android:activity.animationFinishedListener":
+                    // KEY_ANIMATION_FINISHED_LISTENER
+                case "android:activity.animSpecs": // KEY_ANIM_SPECS
+                case "android:activity.lockTaskMode": // KEY_LOCK_TASK_MODE
+                case "android:activity.shareIdentity": // KEY_SHARE_IDENTITY
+                case "android.activity.launchDisplayId": // KEY_LAUNCH_DISPLAY_ID
+                case "android.activity.callerDisplayId": // KEY_CALLER_DISPLAY_ID
+                case "android.activity.launchTaskDisplayAreaToken":
+                    // KEY_LAUNCH_TASK_DISPLAY_AREA_TOKEN
+                case "android.activity.launchTaskDisplayAreaFeatureId":
+                    // KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID
+                case "android.activity.windowingMode": // KEY_LAUNCH_WINDOWING_MODE
+                case "android.activity.activityType": // KEY_LAUNCH_ACTIVITY_TYPE
+                case "android.activity.launchTaskId": // KEY_LAUNCH_TASK_ID
+                case "android.activity.disableStarting": // KEY_DISABLE_STARTING_WINDOW
+                case "android.activity.pendingIntentLaunchFlags":
+                    // KEY_PENDING_INTENT_LAUNCH_FLAGS
+                case "android.activity.alwaysOnTop": // KEY_TASK_ALWAYS_ON_TOP
+                case "android.activity.taskOverlay": // KEY_TASK_OVERLAY
+                case "android.activity.taskOverlayCanResume": // KEY_TASK_OVERLAY_CAN_RESUME
+                case "android.activity.avoidMoveToFront": // KEY_AVOID_MOVE_TO_FRONT
+                case "android.activity.freezeRecentTasksReordering":
+                    // KEY_FREEZE_RECENT_TASKS_REORDERING
+                case "android:activity.disallowEnterPictureInPictureWhileLaunching":
+                    // KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING
+                case "android:activity.applyActivityFlagsForBubbles":
+                    // KEY_APPLY_ACTIVITY_FLAGS_FOR_BUBBLES
+                case "android:activity.applyMultipleTaskFlagForShortcut":
+                    // KEY_APPLY_MULTIPLE_TASK_FLAG_FOR_SHORTCUT
+                case "android:activity.applyNoUserActionFlagForShortcut":
+                    // KEY_APPLY_NO_USER_ACTION_FLAG_FOR_SHORTCUT
+                case "android:activity.transitionCompleteListener":
+                    // KEY_TRANSITION_COMPLETE_LISTENER
+                case "android:activity.transitionIsReturning": // KEY_TRANSITION_IS_RETURNING
+                case "android:activity.sharedElementNames": // KEY_TRANSITION_SHARED_ELEMENTS
+                case "android:activity.resultData": // KEY_RESULT_DATA
+                case "android:activity.resultCode": // KEY_RESULT_CODE
+                case "android:activity.exitCoordinatorIndex": // KEY_EXIT_COORDINATOR_INDEX
+                case "android.activity.sourceInfo": // KEY_SOURCE_INFO
+                case "android:activity.usageTimeReport": // KEY_USAGE_TIME_REPORT
+                case "android:activity.rotationAnimationHint": // KEY_ROTATION_ANIMATION_HINT
+                case "android:instantapps.installerbundle": // KEY_INSTANT_APP_VERIFICATION_BUNDLE
+                case "android:activity.specsFuture": // KEY_SPECS_FUTURE
+                case "android:activity.remoteAnimationAdapter": // KEY_REMOTE_ANIMATION_ADAPTER
+                case "android:activity.remoteTransition": // KEY_REMOTE_TRANSITION
+                case "android:activity.overrideTaskTransition": // KEY_OVERRIDE_TASK_TRANSITION
+                case "android.activity.removeWithTaskOrganizer": // KEY_REMOVE_WITH_TASK_ORGANIZER
+                case "android.activity.launchTypeBubble": // KEY_LAUNCHED_FROM_BUBBLE
+                case "android.activity.splashScreenStyle": // KEY_SPLASH_SCREEN_STYLE
+                case "android.activity.launchIntoPipParams": // KEY_LAUNCH_INTO_PIP_PARAMS
+                case "android.activity.dismissKeyguard": // KEY_DISMISS_KEYGUARD
+                case "android.activity.pendingIntentCreatorBackgroundActivityStartMode":
+                    // KEY_PENDING_INTENT_CREATOR_BACKGROUND_ACTIVITY_START_MODE
+                case "android.activity.launchCookie": // KEY_LAUNCH_COOKIE
+                    // Existing keys
+                    break;
+                default:
+                    unknownKeys.add(key);
+                    break;
+            }
+        }
+
+        // Report if any unknown key exists.
+        for (String key : unknownKeys) {
+            Log.e("ActivityOptionsTests", "Unknown key " + key + " is found. "
+                    + "Please review if the given bundle should be protected with permissions.");
+        }
+        assertTrue(unknownKeys.isEmpty());
+    }
+
     public static class TrampolineActivity extends Activity {
         static int sTaskId;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 3934b02..4034dbc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -62,28 +62,31 @@
     @Test
     public void testPostLayout() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        statusBar.setBounds(0, 0, 500, 1000);
         statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
         mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateSourceFrame(statusBar.getFrame());
         mProvider.onPostLayout();
         assertEquals(new Rect(0, 0, 500, 100), mProvider.getSource().getFrame());
-        assertEquals(Insets.of(0, 100, 0, 0),
-                mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
-                        false /* ignoreVisibility */));
-        assertEquals(Insets.of(0, 100, 0, 0),
-                mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
+        assertEquals(Insets.of(0, 100, 0, 0), mProvider.getInsetsHint());
+
+        // Change the bounds and call onPostLayout. Make sure the insets hint gets updated.
+        statusBar.setBounds(0, 10, 500, 1000);
+        mProvider.onPostLayout();
+        assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
     }
 
     @Test
     public void testPostLayout_invisible() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        statusBar.setBounds(0, 0, 500, 1000);
         statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindowContainer(statusBar, null, null);
         mProvider.updateSourceFrame(statusBar.getFrame());
         mProvider.onPostLayout();
-        assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
-                false /* ignoreVisibility */));
+        assertTrue(mProvider.getSource().getFrame().isEmpty());
+        assertEquals(Insets.NONE, mProvider.getInsetsHint());
     }
 
     @Test
@@ -160,6 +163,36 @@
     }
 
     @Test
+    public void testUpdateSourceFrame() {
+        final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+        mProvider.setWindowContainer(statusBar, null, null);
+        statusBar.setBounds(0, 0, 500, 1000);
+
+        mProvider.setServerVisible(true);
+        statusBar.getFrame().set(0, 0, 500, 100);
+        mProvider.updateSourceFrame(statusBar.getFrame());
+        assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 100, 0, 0), mProvider.getInsetsHint());
+
+        // Only change the source frame but not the visibility.
+        statusBar.getFrame().set(0, 0, 500, 90);
+        mProvider.updateSourceFrame(statusBar.getFrame());
+        assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
+
+        mProvider.setServerVisible(false);
+        statusBar.getFrame().set(0, 0, 500, 80);
+        mProvider.updateSourceFrame(statusBar.getFrame());
+        assertTrue(mProvider.getSource().getFrame().isEmpty());
+        assertEquals(Insets.of(0, 90, 0, 0), mProvider.getInsetsHint());
+
+        // Only change the visibility but not the frame.
+        mProvider.setServerVisible(true);
+        assertEquals(statusBar.getFrame(), mProvider.getSource().getFrame());
+        assertEquals(Insets.of(0, 80, 0, 0), mProvider.getInsetsHint());
+    }
+
+    @Test
     public void testUpdateSourceFrameForIme() {
         final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
 
@@ -184,7 +217,7 @@
     }
 
     @Test
-    public void testInsetsModified() {
+    public void testSetRequestedVisibleTypes() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
@@ -196,7 +229,7 @@
     }
 
     @Test
-    public void testInsetsModified_noControl() {
+    public void testSetRequestedVisibleTypes_noControl() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrame().set(0, 0, 500, 100);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
index 51a7e74..06033c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationPersisterTest.java
@@ -20,7 +20,6 @@
 
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
-import static com.android.server.wm.LetterboxConfigurationPersister.LETTERBOX_CONFIGURATION_FILENAME;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -42,13 +41,26 @@
 import java.io.File;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
+import java.util.function.Supplier;
 
+/**
+ * Tests for the {@link LetterboxConfigurationPersister} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:LetterboxConfigurationPersisterTest
+ */
 @SmallTest
 @Presubmit
 public class LetterboxConfigurationPersisterTest {
 
     private static final long TIMEOUT = 2000L; // 2 secs
 
+    private static final int DEFAULT_REACHABILITY_TEST = -1;
+    private static final Supplier<Integer> DEFAULT_REACHABILITY_SUPPLIER_TEST =
+            () -> DEFAULT_REACHABILITY_TEST;
+
+    private static final String LETTERBOX_CONFIGURATION_TEST_FILENAME = "letterbox_config_test";
+
     private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
     private Context mContext;
     private PersisterQueue mPersisterQueue;
@@ -62,7 +74,7 @@
         mConfigFolder = mContext.getFilesDir();
         mPersisterQueue = new PersisterQueue();
         mQueueState = new QueueState();
-        mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(mContext,
+        mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(
                 () -> mContext.getResources().getInteger(
                         R.integer.config_letterboxDefaultPositionForHorizontalReachability),
                 () -> mContext.getResources().getInteger(
@@ -72,7 +84,8 @@
                 () -> mContext.getResources().getInteger(
                         R.integer.config_letterboxDefaultPositionForTabletopModeReachability
                 ),
-                mConfigFolder, mPersisterQueue, mQueueState);
+                mConfigFolder, mPersisterQueue, mQueueState,
+                LETTERBOX_CONFIGURATION_TEST_FILENAME);
         mQueueListener = queueEmpty -> mQueueState.onItemAdded();
         mPersisterQueue.addListener(mQueueListener);
         mLetterboxConfigurationPersister.start();
@@ -127,8 +140,10 @@
     public void test_whenUpdatedWithNewValues_valuesAreReadAfterRestart() {
         final PersisterQueue firstPersisterQueue = new PersisterQueue();
         final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
-                mContext, () -> -1, () -> -1, () -> -1, () -> -1, mContext.getFilesDir(),
-                firstPersisterQueue, mQueueState);
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                mContext.getFilesDir(), firstPersisterQueue, mQueueState,
+                LETTERBOX_CONFIGURATION_TEST_FILENAME);
         firstPersister.start();
         firstPersister.setLetterboxPositionForHorizontalReachability(false,
                 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
@@ -138,8 +153,10 @@
         stopPersisterSafe(firstPersisterQueue);
         final PersisterQueue secondPersisterQueue = new PersisterQueue();
         final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
-                mContext, () -> -1, () -> -1, () -> -1, () -> -1, mContext.getFilesDir(),
-                secondPersisterQueue, mQueueState);
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                mContext.getFilesDir(), secondPersisterQueue, mQueueState,
+                LETTERBOX_CONFIGURATION_TEST_FILENAME);
         secondPersister.start();
         final int newPositionForHorizontalReachability =
                 secondPersister.getLetterboxPositionForHorizontalReachability(false);
@@ -156,37 +173,46 @@
 
     @Test
     public void test_whenUpdatedWithNewValuesAndDeleted_valuesAreDefaults() {
-        mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(false,
+        final PersisterQueue firstPersisterQueue = new PersisterQueue();
+        final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                mContext.getFilesDir(), firstPersisterQueue, mQueueState,
+                LETTERBOX_CONFIGURATION_TEST_FILENAME);
+        firstPersister.start();
+        firstPersister.setLetterboxPositionForHorizontalReachability(false,
                 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
-        mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(false,
+        firstPersister.setLetterboxPositionForVerticalReachability(false,
                 LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
         waitForCompletion(mPersisterQueue);
         final int newPositionForHorizontalReachability =
-                mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
-                        false);
+                firstPersister.getLetterboxPositionForHorizontalReachability(false);
         final int newPositionForVerticalReachability =
-                mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
+                firstPersister.getLetterboxPositionForVerticalReachability(false);
         Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
                 newPositionForHorizontalReachability);
         Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
                 newPositionForVerticalReachability);
-        deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue);
-        waitForCompletion(mPersisterQueue);
+        deleteConfiguration(firstPersister, firstPersisterQueue);
+        waitForCompletion(firstPersisterQueue);
+        stopPersisterSafe(firstPersisterQueue);
+
+        final PersisterQueue secondPersisterQueue = new PersisterQueue();
+        final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
+                mContext.getFilesDir(), secondPersisterQueue, mQueueState,
+                LETTERBOX_CONFIGURATION_TEST_FILENAME);
+        secondPersister.start();
         final int positionForHorizontalReachability =
-                mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
-                        false);
-        final int defaultPositionForHorizontalReachability =
-                mContext.getResources().getInteger(
-                        R.integer.config_letterboxDefaultPositionForHorizontalReachability);
-        Assert.assertEquals(defaultPositionForHorizontalReachability,
-                positionForHorizontalReachability);
+                secondPersister.getLetterboxPositionForHorizontalReachability(false);
         final int positionForVerticalReachability =
-                mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
-        final int defaultPositionForVerticalReachability =
-                mContext.getResources().getInteger(
-                        R.integer.config_letterboxDefaultPositionForVerticalReachability);
-        Assert.assertEquals(defaultPositionForVerticalReachability,
-                positionForVerticalReachability);
+                secondPersister.getLetterboxPositionForVerticalReachability(false);
+        Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForHorizontalReachability);
+        Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForVerticalReachability);
+        deleteConfiguration(secondPersister, secondPersisterQueue);
+        waitForCompletion(secondPersisterQueue);
+        stopPersisterSafe(secondPersisterQueue);
     }
 
     private void stopPersisterSafe(PersisterQueue persisterQueue) {
@@ -222,7 +248,7 @@
     private void deleteConfiguration(LetterboxConfigurationPersister persister,
             PersisterQueue persisterQueue) {
         final AtomicFile fileToDelete = new AtomicFile(
-                new File(mConfigFolder, LETTERBOX_CONFIGURATION_FILENAME));
+                new File(mConfigFolder, LETTERBOX_CONFIGURATION_TEST_FILENAME));
         persisterQueue.addItem(
                 new DeleteFileCommand(fileToDelete, mQueueState.andThen(
                         s -> persister.useDefaultValue())), true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index f23e56d..7cb5802 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1303,6 +1303,26 @@
         assertTrue(info.supportsMultiWindow);
     }
 
+    @Test
+    public void testRemoveCompatibleRecentTask() {
+        final Task task1 = createTaskBuilder(".Task").setWindowingMode(
+                WINDOWING_MODE_FULLSCREEN).build();
+        mRecentTasks.add(task1);
+        final Task task2 = createTaskBuilder(".Task").setWindowingMode(
+                WINDOWING_MODE_MULTI_WINDOW).build();
+        mRecentTasks.add(task2);
+        assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+                true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+
+        // Set windowing mode and ensure the same fullscreen task that created earlier is removed.
+        task2.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mRecentTasks.removeCompatibleRecentTask(task2);
+        assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+                true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size());
+        assertEquals(task2.mTaskId, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */,
+                true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().get(0).taskId);
+    }
+
     private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) {
         HardwareBuffer buffer = null;
         if (bufferSize != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index abf21a5..7eab06a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -656,7 +656,7 @@
                 topSplitPrimary.getVisibility(null /* starting */));
         // Make primary split root transient-hide.
         spyOn(splitPrimary.mTransitionController);
-        doReturn(true).when(splitPrimary.mTransitionController).isTransientHide(
+        doReturn(true).when(splitPrimary.mTransitionController).isTransientVisible(
                 organizer.mPrimary);
         // The split root and its top become visible.
         assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
index 24e932f..6c48a69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SafeActivityOptionsTest.java
@@ -16,17 +16,36 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.mock;
 
 import android.app.ActivityOptions;
+import android.content.pm.ActivityInfo;
+import android.os.Looper;
 import android.platform.test.annotations.Presubmit;
+import android.view.RemoteAnimationAdapter;
+import android.window.RemoteTransition;
 import android.window.WindowContainerToken;
 
 import androidx.test.filters.MediumTest;
 
 import org.junit.Test;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 
 /**
  * Build/Install/Run:
@@ -73,4 +92,119 @@
 
         assertSame(clone.getOriginalOptions().getLaunchRootTask(), token);
     }
+
+    @Test
+    public void test_getOptions() {
+        // Mock everything necessary
+        MockitoSession mockingSession = mockitoSession()
+                .mockStatic(ActivityTaskManagerService.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
+        doReturn(PERMISSION_DENIED).when(() -> ActivityTaskManagerService.checkPermission(
+                any(), anyInt(), anyInt()));
+
+        final LockTaskController lockTaskController = mock(LockTaskController.class);
+        doReturn(false).when(lockTaskController).isPackageAllowlisted(anyInt(), any());
+
+        final ActivityTaskManagerService atm = mock(ActivityTaskManagerService.class);
+        doReturn(lockTaskController).when(atm).getLockTaskController();
+
+        final ActivityTaskSupervisor taskSupervisor =
+                new ActivityTaskSupervisor(atm, mock(Looper.class));
+        spyOn(taskSupervisor);
+        doReturn(false).when(taskSupervisor).isCallerAllowedToLaunchOnDisplay(anyInt(),
+                anyInt(), anyInt(), any());
+        doReturn(false).when(taskSupervisor).isCallerAllowedToLaunchOnTaskDisplayArea(anyInt(),
+                anyInt(), any(), any());
+
+        taskSupervisor.mRecentTasks = mock(RecentTasks.class);
+        doReturn(false).when(taskSupervisor.mRecentTasks).isCallerRecents(anyInt());
+
+        // Ensure exceptions are thrown when lack of permissions.
+        ActivityOptions activityOptions = ActivityOptions.makeBasic();
+        try {
+            activityOptions.setLaunchTaskId(100);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setDisableStartingWindow(true);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setTransientLaunch();
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setDismissKeyguard();
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setLaunchActivityType(ACTIVITY_TYPE_STANDARD);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setLaunchedFromBubble(true);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setLaunchDisplayId(DEFAULT_DISPLAY);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setLockTaskEnabled(true);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeCustomTaskAnimation(
+                    getInstrumentation().getContext(), 0, 0, null, null, null);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            RemoteAnimationAdapter remoteAnimationAdapter = mock(RemoteAnimationAdapter.class);
+            RemoteTransition remoteTransition = mock(RemoteTransition.class);
+            activityOptions = ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeRemoteAnimation(remoteAnimationAdapter,
+                    remoteTransition);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setRemoteAnimationAdapter(remoteAnimationAdapter);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeRemoteTransition(remoteTransition);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            activityOptions = ActivityOptions.makeBasic();
+            activityOptions.setRemoteTransition(remoteTransition);
+            verifySecureExceptionThrown(activityOptions, taskSupervisor);
+
+            verifySecureExceptionThrown(activityOptions, taskSupervisor,
+                    mock(TaskDisplayArea.class));
+        } finally {
+            mockingSession.finishMocking();
+        }
+    }
+
+    private void verifySecureExceptionThrown(ActivityOptions activityOptions,
+            ActivityTaskSupervisor taskSupervisor) {
+        verifySecureExceptionThrown(activityOptions, taskSupervisor, null /* mockTda */);
+    }
+
+    private void verifySecureExceptionThrown(ActivityOptions activityOptions,
+            ActivityTaskSupervisor taskSupervisor, TaskDisplayArea mockTda) {
+        SafeActivityOptions safeActivityOptions = new SafeActivityOptions(activityOptions);
+        if (mockTda != null) {
+            spyOn(safeActivityOptions);
+            doReturn(mockTda).when(safeActivityOptions).getLaunchTaskDisplayArea(any(), any());
+        }
+
+        boolean isExceptionThrow = false;
+        final ActivityInfo aInfo = mock(ActivityInfo.class);
+        try {
+            safeActivityOptions.getOptions(null, aInfo, null, taskSupervisor);
+        } catch (SecurityException ex) {
+            isExceptionThrow = true;
+        }
+        assertTrue(isExceptionThrow);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index b181213..fb95748 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -91,7 +91,7 @@
     }
 
     @Test
-    public void testScreenshotSecureLayers() {
+    public void testScreenshotSecureLayers() throws InterruptedException {
         SurfaceControl secureSC = new SurfaceControl.Builder()
                 .setName("SecureChildSurfaceControl")
                 .setBLASTLayer()
@@ -197,6 +197,8 @@
         private static final long WAIT_TIMEOUT_S = 5;
         private final Handler mHandler = new Handler(Looper.getMainLooper());
 
+        private final CountDownLatch mAttachedLatch = new CountDownLatch(1);
+
         @Override
         protected void onCreate(@Nullable Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
@@ -204,7 +206,16 @@
                     PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
         }
 
-        SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
+        @Override
+        public void onAttachedToWindow() {
+            super.onAttachedToWindow();
+            mAttachedLatch.countDown();
+        }
+
+        SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl)
+                throws InterruptedException {
+            assertTrue("Failed to wait for onAttachedToWindow",
+                    mAttachedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
             CountDownLatch countDownLatch = new CountDownLatch(1);
             mHandler.post(() -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 3908947..d5afe3b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -27,6 +27,11 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
@@ -91,9 +96,12 @@
 import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.ScreenOrientation;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
@@ -2255,6 +2263,169 @@
     }
 
     @Test
+    public void testUserOverrideSplitScreenAspectRatioForLandscapeDisplay() {
+        final int displayWidth = 1600;
+        final int displayHeight = 1400;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+        float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
+
+        testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+    }
+
+    @Test
+    public void testUserOverrideSplitScreenAspectRatioForPortraitDisplay() {
+        final int displayWidth = 1400;
+        final int displayHeight = 1600;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+        float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
+
+        testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN);
+    }
+
+    @Test
+    public void testUserOverrideDisplaySizeAspectRatioForLandscapeDisplay() {
+        final int displayWidth = 1600;
+        final int displayHeight = 1400;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+        float expectedAspectRatio = 1f * displayWidth / displayHeight;
+
+        testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+    }
+
+    @Test
+    public void testUserOverrideDisplaySizeAspectRatioForPortraitDisplay() {
+        final int displayWidth = 1400;
+        final int displayHeight = 1600;
+        setUpDisplaySizeWithApp(displayWidth, displayHeight);
+
+        float expectedAspectRatio = 1f * displayHeight / displayWidth;
+
+        testUserOverrideAspectRatio(expectedAspectRatio, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE);
+    }
+
+    @Test
+    public void testUserOverride32AspectRatioForPortraitDisplay() {
+        setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
+        testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
+    }
+
+    @Test
+    public void testUserOverride32AspectRatioForLandscapeDisplay() {
+        setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+        testUserOverrideAspectRatio(3 / 2f, USER_MIN_ASPECT_RATIO_3_2);
+    }
+
+    @Test
+    public void testUserOverride43AspectRatioForPortraitDisplay() {
+        setUpDisplaySizeWithApp(/* dw */ 1400, /* dh */ 1600);
+        testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
+    }
+
+    @Test
+    public void testUserOverride43AspectRatioForLandscapeDisplay() {
+        setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+        testUserOverrideAspectRatio(4 / 3f, USER_MIN_ASPECT_RATIO_4_3);
+    }
+
+    @Test
+    public void testUserOverride169AspectRatioForPortraitDisplay() {
+        setUpDisplaySizeWithApp(/* dw */ 1800, /* dh */ 1500);
+        testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
+    }
+
+    @Test
+    public void testUserOverride169AspectRatioForLandscapeDisplay() {
+        setUpDisplaySizeWithApp(/* dw */ 1500, /* dh */ 1800);
+        testUserOverrideAspectRatio(16 / 9f, USER_MIN_ASPECT_RATIO_16_9);
+    }
+
+    @Test
+    @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+            ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+    public void testUserOverrideAspectRatioOverSystemOverride() {
+        setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+
+        testUserOverrideAspectRatio(false,
+                SCREEN_ORIENTATION_PORTRAIT,
+                3 / 2f,
+                USER_MIN_ASPECT_RATIO_3_2,
+                true);
+    }
+
+    @Test
+    public void testUserOverrideAspectRatioNotEnabled() {
+        setUpDisplaySizeWithApp(/* dw */ 1600, /* dh */ 1400);
+
+        // App aspect ratio doesn't change
+        testUserOverrideAspectRatio(false,
+                SCREEN_ORIENTATION_PORTRAIT,
+                1f * 1600 / 1400,
+                USER_MIN_ASPECT_RATIO_3_2,
+                false);
+    }
+
+    private void testUserOverrideAspectRatio(float expectedAspectRatio,
+            @PackageManager.UserMinAspectRatio int aspectRatio) {
+        testUserOverrideAspectRatio(true,
+                SCREEN_ORIENTATION_PORTRAIT,
+                expectedAspectRatio,
+                aspectRatio,
+                true);
+
+        testUserOverrideAspectRatio(false,
+                SCREEN_ORIENTATION_PORTRAIT,
+                expectedAspectRatio,
+                aspectRatio,
+                true);
+
+        testUserOverrideAspectRatio(true,
+                SCREEN_ORIENTATION_LANDSCAPE,
+                expectedAspectRatio,
+                aspectRatio,
+                true);
+
+        testUserOverrideAspectRatio(false,
+                SCREEN_ORIENTATION_LANDSCAPE,
+                expectedAspectRatio,
+                aspectRatio,
+                true);
+    }
+
+    private void testUserOverrideAspectRatio(boolean isUnresizable, int screenOrientation,
+            float expectedAspectRatio, @PackageManager.UserMinAspectRatio int aspectRatio,
+            boolean enabled) {
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+        activity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
+        activity.mWmService.mLetterboxConfiguration
+                .setUserAppAspectRatioSettingsOverrideEnabled(enabled);
+        // Set user aspect ratio override
+        final IPackageManager pm = mAtm.getPackageManager();
+        try {
+            doReturn(aspectRatio).when(pm)
+                    .getUserMinAspectRatio(activity.packageName, activity.mUserId);
+        } catch (RemoteException ignored) {
+        }
+
+        prepareLimitedBounds(activity, screenOrientation, isUnresizable);
+
+        final Rect afterBounds = activity.getBounds();
+        final int width = afterBounds.width();
+        final int height = afterBounds.height();
+        final float afterAspectRatio =
+                (float) Math.max(width, height) / (float) Math.min(width, height);
+
+        assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+    }
+
+    @Test
     @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
             ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_TO_ALIGN_WITH_SPLIT_SCREEN})
     public void testOverrideSplitScreenAspectRatioForUnresizablePortraitApps() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index ffecafb..5154d17 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1488,6 +1488,47 @@
     }
 
     @Test
+    public void testIsTransientVisible() {
+        final ActivityRecord appB = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setVisible(false).build();
+        final ActivityRecord recent = new ActivityBuilder(mAtm).setCreateTask(true)
+                .setVisible(false).build();
+        final ActivityRecord appA = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        final Task taskA = appA.getTask();
+        final Task taskB = appB.getTask();
+        final Task taskRecent = recent.getTask();
+        registerTestTransitionPlayer();
+        final TransitionController controller = mRootWindowContainer.mTransitionController;
+        final Transition transition = createTestTransition(TRANSIT_OPEN, controller);
+        controller.moveToCollecting(transition);
+        transition.collect(recent);
+        transition.collect(taskA);
+        transition.setTransientLaunch(recent, taskA);
+        taskRecent.moveToFront("move-recent-to-front");
+
+        // During collecting and playing, the recent is on top so it is visible naturally.
+        // While B needs isTransientVisible to keep visibility because it is occluded by recents.
+        assertFalse(controller.isTransientVisible(taskB));
+        assertTrue(controller.isTransientVisible(taskA));
+        assertFalse(controller.isTransientVisible(taskRecent));
+        // Switch to playing state.
+        transition.onTransactionReady(transition.getSyncId(), mMockT);
+        assertTrue(controller.isTransientVisible(taskA));
+
+        // Switch to another task. For example, use gesture navigation to switch tasks.
+        taskB.moveToFront("move-b-to-front");
+        // The previous app (taskA) should be paused first so it loses transient visible. Because
+        // visually it is taskA -> taskB, the pause -> resume order should be the same.
+        assertFalse(controller.isTransientVisible(taskA));
+        // Keep the recent visible so there won't be 2 activities pausing at the same time. It is
+        // to avoid the latency to resume the current top, i.e. appB.
+        assertTrue(controller.isTransientVisible(taskRecent));
+        // The recent is paused after the transient transition is finished.
+        controller.finishTransition(transition);
+        assertFalse(controller.isTransientVisible(taskRecent));
+    }
+
+    @Test
     public void testNotReadyPushPop() {
         final TransitionController controller = new TestTransitionController(mAtm);
         controller.setSyncEngine(mWm.mSyncEngine);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index 248cc26..ccc4ac2 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -27,6 +27,7 @@
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER;
 import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__SERVICE_CRASH;
 
+import android.app.AppOpsManager;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.ChangeId;
@@ -548,13 +549,15 @@
     static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
         private final HotwordDetectionConnection mHotwordDetectionConnection;
         private final IHotwordRecognitionStatusCallback mExternalCallback;
-        private final int mVoiceInteractionServiceUid;
+        private final Identity mVoiceInteractorIdentity;
+        private final Context mContext;
 
-        SoundTriggerCallback(IHotwordRecognitionStatusCallback callback,
-                HotwordDetectionConnection connection, int uid) {
+        SoundTriggerCallback(Context context, IHotwordRecognitionStatusCallback callback,
+                HotwordDetectionConnection connection, Identity voiceInteractorIdentity) {
+            mContext = context;
             mHotwordDetectionConnection = connection;
             mExternalCallback = callback;
-            mVoiceInteractionServiceUid = uid;
+            mVoiceInteractorIdentity = voiceInteractorIdentity;
         }
 
         @Override
@@ -568,15 +571,30 @@
                 HotwordMetricsLogger.writeKeyphraseTriggerEvent(
                         HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP,
                         HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
-                        mVoiceInteractionServiceUid);
+                        mVoiceInteractorIdentity.uid);
                 mHotwordDetectionConnection.detectFromDspSource(
                         recognitionEvent, mExternalCallback);
             } else {
-                HotwordMetricsLogger.writeKeyphraseTriggerEvent(
-                        HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
-                        HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
-                        mVoiceInteractionServiceUid);
-                mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
+                // We have to attribute ops here, since we configure all st clients as trusted to
+                // enable a partial exemption.
+                // TODO (b/292012931) remove once trusted uniformly required.
+                int result = mContext.getSystemService(AppOpsManager.class)
+                        .noteOpNoThrow(AppOpsManager.OP_RECORD_AUDIO_HOTWORD,
+                            mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName,
+                            mVoiceInteractorIdentity.attributionTag,
+                            "Non-HDS keyphrase recognition to VoiceInteractionService");
+
+                if (result != AppOpsManager.MODE_ALLOWED) {
+                    Slog.w(TAG, "onKeyphraseDetected suppressed, permission check returned: "
+                            + result);
+                    mExternalCallback.onRecognitionPaused();
+                } else {
+                    HotwordMetricsLogger.writeKeyphraseTriggerEvent(
+                            HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR,
+                            HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER,
+                            mVoiceInteractorIdentity.uid);
+                    mExternalCallback.onKeyphraseDetected(recognitionEvent, null);
+                }
             }
         }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 605af03..3502a3f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -104,6 +104,7 @@
 import com.android.server.UiThread;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.LegacyPermissionManagerInternal;
+import com.android.server.policy.AppOpsPolicy;
 import com.android.server.utils.Slogf;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -336,6 +337,9 @@
         /** The start value of showSessionId */
         private static final int SHOW_SESSION_START_ID = 0;
 
+        private final boolean IS_HDS_REQUIRED = AppOpsPolicy.isHotwordDetectionServiceRequired(
+                mContext.getPackageManager());
+
         @GuardedBy("this")
         private int mShowSessionId = SHOW_SESSION_START_ID;
 
@@ -393,8 +397,14 @@
             }
             try (SafeCloseable ignored = PermissionUtil.establishIdentityDirect(
                     originatorIdentity)) {
+                if (!IS_HDS_REQUIRED) {
+                    // For devices which still have hotword exemption, any client (not just HDS
+                    // clients) are trusted.
+                    // TODO (b/292012931) remove once trusted uniformly required.
+                    forHotwordDetectionService = true;
+                }
                 return new SoundTriggerSession(mSoundTriggerInternal.attach(client,
-                            moduleProperties, forHotwordDetectionService));
+                            moduleProperties, forHotwordDetectionService), originatorIdentity);
             }
         }
 
@@ -1674,10 +1684,13 @@
             final SoundTriggerInternal.Session mSession;
             private IHotwordRecognitionStatusCallback mSessionExternalCallback;
             private IRecognitionStatusCallback mSessionInternalCallback;
+            private final Identity mVoiceInteractorIdentity;
 
             SoundTriggerSession(
-                    SoundTriggerInternal.Session session) {
+                    SoundTriggerInternal.Session session,
+                    Identity voiceInteractorIdentity) {
                 mSession = session;
+                mVoiceInteractorIdentity = voiceInteractorIdentity;
             }
 
             @Override
@@ -1731,7 +1744,8 @@
                         if (mSessionExternalCallback == null
                                 || mSessionInternalCallback == null
                                 || callback.asBinder() != mSessionExternalCallback.asBinder()) {
-                            mSessionInternalCallback = createSoundTriggerCallbackLocked(callback);
+                            mSessionInternalCallback = createSoundTriggerCallbackLocked(callback,
+                                    mVoiceInteractorIdentity);
                             mSessionExternalCallback = callback;
                         }
                     }
@@ -1752,7 +1766,8 @@
                     if (mSessionExternalCallback == null
                             || mSessionInternalCallback == null
                             || callback.asBinder() != mSessionExternalCallback.asBinder()) {
-                        soundTriggerCallback = createSoundTriggerCallbackLocked(callback);
+                        soundTriggerCallback = createSoundTriggerCallbackLocked(callback,
+                                mVoiceInteractorIdentity);
                         Slog.w(TAG, "stopRecognition() called with a different callback than"
                                 + "startRecognition()");
                     } else {
@@ -2090,6 +2105,7 @@
                 pw.println("  mTemporarilyDisabled: " + mTemporarilyDisabled);
                 pw.println("  mCurUser: " + mCurUser);
                 pw.println("  mCurUserSupported: " + mCurUserSupported);
+                pw.println("  mIsHdsRequired: " + IS_HDS_REQUIRED);
                 dumpSupportedUsers(pw, "  ");
                 mDbHelper.dump(pw);
                 if (mImpl == null) {
@@ -2165,11 +2181,13 @@
         }
 
         private IRecognitionStatusCallback createSoundTriggerCallbackLocked(
-                IHotwordRecognitionStatusCallback callback) {
+                IHotwordRecognitionStatusCallback callback,
+                Identity voiceInteractorIdentity) {
             if (mImpl == null) {
                 return null;
             }
-            return mImpl.createSoundTriggerCallbackLocked(callback);
+            return mImpl.createSoundTriggerCallbackLocked(mContext, callback,
+                    voiceInteractorIdentity);
         }
 
         class RoleObserver implements OnRoleHoldersChangedListener {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 0ad86c1..5d88a65 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -877,12 +877,13 @@
     }
 
     public IRecognitionStatusCallback createSoundTriggerCallbackLocked(
-            IHotwordRecognitionStatusCallback callback) {
+            Context context, IHotwordRecognitionStatusCallback callback,
+            Identity voiceInteractorIdentity) {
         if (DEBUG) {
             Slog.d(TAG, "createSoundTriggerCallbackLocked");
         }
-        return new HotwordDetectionConnection.SoundTriggerCallback(callback,
-                mHotwordDetectionConnection, mInfo.getServiceInfo().applicationInfo.uid);
+        return new HotwordDetectionConnection.SoundTriggerCallback(context, callback,
+                mHotwordDetectionConnection, voiceInteractorIdentity);
     }
 
     private static ServiceInfo getServiceInfoLocked(@NonNull ComponentName componentName,
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index efcbd31..f1ee76e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5268,7 +5268,11 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+                String[] mergedImsisFromGroup =
+                        telephony.getMergedImsisFromGroup(getSubId(), getOpPackageName());
+                if (mergedImsisFromGroup != null) {
+                    return mergedImsisFromGroup;
+                }
             }
         } catch (RemoteException ex) {
         }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 0f406fd..845e649 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -70,6 +70,9 @@
 
     @Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {}
 
+    @FlakyTest(bugId = 291575593)
+    override fun entireScreenCovered() {}
+
     @Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {}
 
     @Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
index 144a7312..6722cc8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt
@@ -62,14 +62,6 @@
         }
     }
 
-    /** Assert the background animation layer is never visible during bounds change transition. */
-    @Presubmit
-    @Test
-    fun backgroundLayerNeverVisible() {
-        val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
-        flicker.assertLayers { isInvisible(backgroundColorLayer) }
-    }
-
     /** Trampoline activity should finish itself before the end of this test. */
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
new file mode 100644
index 0000000..0417f9d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/pip/SecondaryActivityEnterPipTest.kt
@@ -0,0 +1,188 @@
+/*
+ * 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.wm.flicker.activityembedding
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.common.traces.component.ComponentNameMatcher.Companion.TRANSITION_SNAPSHOT
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a secondary Activity into Picture-In-Picture mode.
+ *
+ * Setup: Start from a split A|B.
+ * Transition: B enters PIP, observe the window shrink to the bottom right corner on screen.
+ *
+ * To run this test: `atest FlickerTests:SecondaryActivityEnterPipTest`
+ *
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class SecondaryActivityEnterPipTest (flicker: LegacyFlickerTest) :
+        ActivityEmbeddingTestBase(flicker) {
+    override val transition: FlickerBuilder.() -> Unit = {
+        setup {
+            tapl.setExpectedRotationCheckEnabled(false)
+            testApp.launchViaIntent(wmHelper)
+            testApp.launchSecondaryActivity(wmHelper)
+            startDisplayBounds =
+                    wmHelper.currentState.layerState.physicalDisplayBounds
+                            ?: error("Can't get display bounds")
+        }
+        transitions {
+            testApp.secondaryActivityEnterPip(wmHelper)
+        }
+        teardown {
+            tapl.goHome()
+            testApp.exit(wmHelper)
+        }
+    }
+
+    /**
+     * Main and secondary activity start from a split each taking half of the screen.
+     */
+    @Presubmit
+    @Test
+    fun layersStartFromEqualSplit() {
+        flicker.assertLayersStart {
+            val leftLayerRegion =
+                    visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+            val rightLayerRegion =
+                    visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+            // Compare dimensions of two splits, given we're using default split attributes,
+            // both activities take up the same visible size on the display.
+            check { "height" }
+                    .that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height)
+            check { "width" }
+                    .that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width)
+            leftLayerRegion.notOverlaps(rightLayerRegion.region)
+            leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds)
+        }
+        flicker.assertLayersEnd {
+            visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+                    .coversExactly(startDisplayBounds)
+        }
+    }
+
+    /**
+     * Main Activity is visible throughout the transition and becomes fullscreen.
+     */
+    @Presubmit
+    @Test
+    fun mainActivityWindowBecomesFullScreen() {
+        flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+        flicker.assertWmEnd {
+            visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+                    .coversExactly(startDisplayBounds)
+        }
+    }
+
+    /**
+     * Main Activity is visible throughout the transition and becomes fullscreen.
+     */
+    @Presubmit
+    @Test
+    fun mainActivityLayerBecomesFullScreen() {
+        flicker.assertLayers {
+            isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+                    .then()
+                    .isVisible(TRANSITION_SNAPSHOT)
+                    .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+                    .then()
+                    .isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+        }
+        flicker.assertLayersEnd {
+            visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+                    .coversExactly(startDisplayBounds)
+        }
+    }
+
+    /**
+     * Secondary Activity is visible throughout the transition and shrinks to the bottom right
+     * corner.
+     */
+    @Presubmit
+    @Test
+    fun secondaryWindowShrinks() {
+        flicker.assertWm {
+            isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+        }
+        flicker.assertWmEnd {
+            val pipWindowRegion =
+                    visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+            check{"height"}
+                    .that(pipWindowRegion.region.height)
+                    .isLower(startDisplayBounds.height / 2)
+            check{"width"}
+                    .that(pipWindowRegion.region.width).isLower(startDisplayBounds.width)
+        }
+    }
+
+    /**
+     * During the transition Secondary Activity shrinks to the bottom right corner.
+     */
+    @Presubmit
+    @Test
+    fun secondaryLayerShrinks() {
+        flicker.assertLayers {
+            val pipLayerList = layers {
+                ComponentNameMatcher.PIP_CONTENT_OVERLAY.layerMatchesAnyOf(it) && it.isVisible
+            }
+            pipLayerList.zipWithNext { previous, current ->
+                // TODO(b/290987990): Add checks for visibleRegion.
+                current.screenBounds.isToTheRightBottom(previous.screenBounds.region, 3)
+                current.screenBounds.notBiggerThan(previous.screenBounds.region)
+            }
+        }
+        flicker.assertLayersEnd {
+            val pipRegion = visibleRegion(
+                    ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+            check { "height" }
+                    .that(pipRegion.region.height)
+                    .isLower(startDisplayBounds.height / 2)
+            check { "width" }
+                    .that(pipRegion.region.width).isLower(startDisplayBounds.width)
+        }
+    }
+
+    companion object {
+        /** {@inheritDoc} */
+        private var startDisplayBounds = Rect.EMPTY
+        /**
+         * Creates the test configurations.
+         *
+         * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+         * navigation modes.
+         */
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index a72ec1e..2d3bc2d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -113,6 +113,21 @@
             .waitForAndVerify()
     }
 
+    fun secondaryActivityEnterPip(wmHelper: WindowManagerStateHelper) {
+        val pipButton =
+                uiDevice.wait(
+                        Until.findObject(By.res(getPackage(), "secondary_enter_pip_button")),
+                        FIND_TIMEOUT
+                )
+        require(pipButton != null) { "Can't find enter pip button on screen." }
+        pipButton.click()
+        wmHelper
+                .StateSyncBuilder()
+                .withAppTransitionIdle()
+                .withPipShown()
+                .waitForAndVerify()
+    }
+
     /**
      * Clicks the button to launch a secondary activity with alwaysExpand enabled, which will launch
      * a fullscreen window on top of the visible region.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index 24e231c..82de646 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -383,8 +383,11 @@
                     it.wmState.visibleWindows.firstOrNull { window ->
                         this.windowMatchesAnyOf(window)
                     }
-                        ?: return@add false
+                Log.d(TAG, "window " + pipAppWindow)
+                if (pipAppWindow == null) return@add false
                 val pipRegion = pipAppWindow.frameRegion
+                Log.d(TAG, "region " + pipRegion +
+                        " covers " + windowRect.coversMoreThan(pipRegion))
                 return@add windowRect.coversMoreThan(pipRegion)
             }
             .waitForAndVerify()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index c1db423..ea7c7c4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -25,6 +25,7 @@
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.LegacyFlickerTest
 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.BaseTest
 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.helpers.setRotation
@@ -78,6 +79,7 @@
         flicker.snapshotStartingWindowLayerCoversExactlyOnApp(imeTestApp)
     }
 
+    @FlakyTest(bugId = 290767483)
     @Postsubmit
     @Test
     fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 7a16060..94b090f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -200,7 +200,7 @@
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
-    @FlakyTest(bugId = 251217585)
+    @FlakyTest(bugId = 285980483)
     @Test
     override fun focusChanges() {
         super.focusChanges()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 4164c0d..df9780e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -21,6 +21,7 @@
 import android.content.res.Resources
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
 import android.tools.common.traces.component.ComponentNameMatcher
 import android.tools.common.traces.component.ComponentNameMatcher.Companion.SPLASH_SCREEN
 import android.tools.common.traces.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -190,6 +191,16 @@
         }
     }
 
+    @Presubmit
+    @Test
+    override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+        flicker.assertLayers {
+            this.visibleLayersShownMoreThanOneConsecutiveEntry(
+                VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(launchNewTaskApp)
+            )
+        }
+    }
+
     companion object {
         private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher {
             val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 68ae806..ff9799a 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -102,6 +102,24 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
+        <activity android:name=".LaunchTransparentActivity"
+                  android:resizeableActivity="false"
+                  android:screenOrientation="portrait"
+                  android:theme="@android:style/Theme"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchTransparentActivity"
+                  android:label="LaunchTransparentActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".TransparentActivity"
+                  android:theme="@style/TransparentTheme"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.TransparentActivity"
+                  android:label="TransparentActivity"
+                  android:exported="false">
+        </activity>
         <activity android:name=".LaunchNewActivity"
                   android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
                   android:theme="@style/CutoutShortEdges"
@@ -206,6 +224,7 @@
             android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
             android:theme="@style/CutoutShortEdges"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+            android:supportsPictureInPicture="true"
             android:exported="false"/>
         <activity
             android:name=".ActivityEmbeddingThirdActivity"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
index 6731446..135140a 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
@@ -35,4 +35,10 @@
       android:onClick="launchThirdActivity"
       android:text="Launch a third activity" />
 
+  <Button
+      android:id="@+id/secondary_enter_pip_button"
+      android:layout_width="wrap_content"
+      android:layout_height="48dp"
+      android:text="Enter pip" />
+
 </LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
similarity index 72%
rename from packages/SystemUI/res/values/dimens_tv.xml
rename to tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
index 3dbd990..0730ded 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2020 The Android Open Source Project
+  ~ 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.
@@ -14,7 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<resources>
-    <dimen name="tv_notification_panel_width">360dp</dimen>
-    <dimen name="tv_notification_blur_radius">31dp</dimen>
-</resources>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+</FrameLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
new file mode 100644
index 0000000..ff4ead9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_transparent_launch.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/black">
+
+        <Button
+            android:id="@+id/button_launch_transparent"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:text="Launch Transparent" />
+        <Button
+            android:id="@+id/button_request_permission"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_centerVertical="true"
+            android:text="Request Permission" />
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
index 1d21fd5..e51ed29 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/values/styles.xml
@@ -43,6 +43,13 @@
         <item name="android:windowSoftInputMode">stateUnchanged</item>
     </style>
 
+    <style name="TransparentTheme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:windowIsTranslucent">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:backgroundDimEnabled">false</item>
+    </style>
+
     <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowDisablePreview">true</item>
     </style>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index dc21027..ee087ef 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.app.PictureInPictureParams;
 import android.graphics.Color;
 import android.os.Bundle;
 import android.view.View;
@@ -40,6 +41,16 @@
                         finish();
                     }
             });
+        findViewById(R.id.secondary_enter_pip_button).setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        PictureInPictureParams.Builder picInPicParamsBuilder =
+                                new PictureInPictureParams.Builder();
+                        enterPictureInPictureMode(picInPicParamsBuilder.build());
+                    }
+                }
+        );
     }
 
     public void launchThirdActivity(View view) {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 95c86ac..2795a6c 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -73,6 +73,18 @@
                 FLICKER_APP_PACKAGE + ".NonResizeablePortraitActivity");
     }
 
+    public static class TransparentActivity {
+        public static final String LABEL = "TransparentActivity";
+        public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+                FLICKER_APP_PACKAGE + ".TransparentActivity");
+    }
+
+    public static class LaunchTransparentActivity {
+        public static final String LABEL = "LaunchTransparentActivity";
+        public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+                FLICKER_APP_PACKAGE + ".LaunchTransparentActivity");
+    }
+
     public static class DialogThemedActivity {
         public static final String LABEL = "DialogThemedActivity";
         public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java
new file mode 100644
index 0000000..7c161fd
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchTransparentActivity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class LaunchTransparentActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.activity_transparent_launch);
+        findViewById(R.id.button_launch_transparent)
+                .setOnClickListener(v -> launchTransparentActivity());
+    }
+
+    private void launchTransparentActivity() {
+        startActivity(new Intent(this, TransparentActivity.class));
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
rename to tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
index 23f37ec..1bac8bd 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/TransparentActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * 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.
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.systemui.tv;
+package com.android.server.wm.flicker.testapp;
 
-import com.android.systemui.dagger.GlobalRootComponent;
+import android.app.Activity;
+import android.os.Bundle;
 
-import dagger.Binds;
-import dagger.Module;
+public class TransparentActivity extends Activity {
 
-@Module
-interface TvSystemUIBinder {
-    @Binds
-    GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent);
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.activity_transparent);
+    }
 }
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 44da69c..5719273 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -100,6 +100,7 @@
 
     private fun clickCloseAppOnAnrDialog() {
         // Find anr dialog and kill app
+        val timestamp = System.currentTimeMillis()
         val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
         val closeAppButton: UiObject2? =
                 uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
@@ -107,7 +108,6 @@
             fail("Could not find anr dialog")
             return
         }
-        val initialReasons = getExitReasons()
         closeAppButton.click()
         /**
          * We must wait for the app to be fully closed before exiting this test. This is because
@@ -116,7 +116,7 @@
          * the killing logic will apply to the newly launched 'am start' instance, and the second
          * test will fail because the unresponsive activity will never be launched.
          */
-        waitForNewExitReason(initialReasons[0].timestamp)
+        waitForNewExitReasonAfter(timestamp)
     }
 
     private fun clickWaitOnAnrDialog() {
@@ -140,12 +140,13 @@
         return infos
     }
 
-    private fun waitForNewExitReason(previousExitTimestamp: Long) {
+    private fun waitForNewExitReasonAfter(timestamp: Long) {
         PollingCheck.waitFor {
-            getExitReasons()[0].timestamp > previousExitTimestamp
+            val reasons = getExitReasons()
+            !reasons.isEmpty() && reasons[0].timestamp >= timestamp
         }
         val reasons = getExitReasons()
-        assertTrue(reasons[0].timestamp > previousExitTimestamp)
+        assertTrue(reasons[0].timestamp > timestamp)
         assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
     }
 
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
index 0c267b2..320daee 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/DefaultImeVisibilityTest.java
@@ -17,8 +17,9 @@
 package com.android.inputmethod.stresstest;
 
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
 
+import static com.android.compatibility.common.util.SystemUtil.eventually;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.REQUEST_FOCUS_ON_CREATE;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.TestActivity.createIntent;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.callOnMainSync;
@@ -26,11 +27,16 @@
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsHidden;
 import static com.android.inputmethod.stresstest.ImeStressTestUtil.waitOnMainUntilImeIsShown;
 
+import static com.google.common.truth.Truth.assertWithMessage;
+
 import android.content.Intent;
 import android.platform.test.annotations.RootPermissionTest;
 import android.platform.test.rule.UnlockScreenRule;
+import android.support.test.uiautomator.UiDevice;
 import android.widget.EditText;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -39,6 +45,7 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Test IME visibility by using system default IME to ensure the behavior is consistent
@@ -59,8 +66,12 @@
     public ScreenCaptureRule mScreenCaptureRule =
             new ScreenCaptureRule("/sdcard/InputMethodStressTest");
 
+    private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(3);
+
     private static final int NUM_TEST_ITERATIONS = 10;
 
+    private final boolean mIsPortrait;
+
     @Parameterized.Parameters(name = "isPortrait={0}")
     public static List<Boolean> isPortraitCases() {
         // Test in both portrait and landscape mode.
@@ -68,6 +79,7 @@
     }
 
     public DefaultImeVisibilityTest(boolean isPortrait) {
+        mIsPortrait = isPortrait;
         mImeStressTestRule.setIsPortrait(isPortrait);
     }
 
@@ -75,14 +87,26 @@
     public void showHideDefaultIme() {
         Intent intent =
                 createIntent(
-                        0x0, /* No window focus flags */
-                        SOFT_INPUT_STATE_UNSPECIFIED | SOFT_INPUT_ADJUST_RESIZE,
+                        0x0 /* No window focus flags */,
+                        SOFT_INPUT_STATE_HIDDEN | SOFT_INPUT_ADJUST_RESIZE,
                         Collections.singletonList(REQUEST_FOCUS_ON_CREATE));
         ImeStressTestUtil.TestActivity activity = ImeStressTestUtil.TestActivity.start(intent);
         EditText editText = activity.getEditText();
+
+        UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        eventually(
+                () ->
+                        assertWithMessage("Display rotation should be updated.")
+                                .that(uiDevice.getDisplayRotation())
+                                .isEqualTo(mIsPortrait ? 0 : 1),
+                TIMEOUT);
+
         for (int i = 0; i < NUM_TEST_ITERATIONS; i++) {
-            callOnMainSync(activity::showImeWithInputMethodManager);
+            // TODO(b/291752364): Remove the explicit focus request once the issue with view focus
+            //  change between fullscreen IME and actual editText is fixed.
+            callOnMainSync(editText::requestFocus);
             verifyWindowAndViewFocus(editText, true, true);
+            callOnMainSync(activity::showImeWithInputMethodManager);
             waitOnMainUntilImeIsShown(editText);
 
             callOnMainSync(activity::hideImeWithInputMethodManager);
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
index 12104b2..c746321 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestRule.java
@@ -20,9 +20,9 @@
 import android.os.RemoteException;
 import android.support.test.uiautomator.UiDevice;
 
+import androidx.annotation.NonNull;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import org.checkerframework.checker.nullness.qual.NonNull;
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
 
@@ -35,8 +35,6 @@
 public class ImeStressTestRule extends TestWatcher {
     private static final String LOCK_SCREEN_OFF_COMMAND = "locksettings set-disabled true";
     private static final String LOCK_SCREEN_ON_COMMAND = "locksettings set-disabled false";
-    private static final String SET_PORTRAIT_MODE_COMMAND = "settings put system user_rotation 0";
-    private static final String SET_LANDSCAPE_MODE_COMMAND = "settings put system user_rotation 1";
     private static final String SIMPLE_IME_ID =
             "com.android.apps.inputmethod.simpleime/.SimpleInputMethodService";
     private static final String ENABLE_IME_COMMAND = "ime enable " + SIMPLE_IME_ID;
@@ -44,8 +42,10 @@
     private static final String DISABLE_IME_COMMAND = "ime disable " + SIMPLE_IME_ID;
     private static final String RESET_IME_COMMAND = "ime reset";
 
-    @NonNull private final Instrumentation mInstrumentation;
-    @NonNull private final UiDevice mUiDevice;
+    @NonNull
+    private final Instrumentation mInstrumentation;
+    @NonNull
+    private final UiDevice mUiDevice;
     // Whether the screen orientation is set to portrait.
     private boolean mIsPortrait;
     // Whether to use a simple test Ime or system default Ime for test.
@@ -105,12 +105,13 @@
     private void setOrientation() {
         try {
             mUiDevice.freezeRotation();
-            executeShellCommand(
-                    mIsPortrait ? SET_PORTRAIT_MODE_COMMAND : SET_LANDSCAPE_MODE_COMMAND);
-        } catch (IOException e) {
-            throw new RuntimeException("Could not set screen orientation.", e);
+            if (mIsPortrait) {
+                mUiDevice.setOrientationNatural();
+            } else {
+                mUiDevice.setOrientationLeft();
+            }
         } catch (RemoteException e) {
-            throw new RuntimeException("Could not freeze rotation.", e);
+            throw new RuntimeException("Could not freeze rotation or set screen orientation.", e);
         }
     }
 
@@ -147,7 +148,8 @@
         }
     }
 
-    private @NonNull String executeShellCommand(@NonNull String cmd) throws IOException {
+    @NonNull
+    private String executeShellCommand(@NonNull String cmd) throws IOException {
         return mUiDevice.executeShellCommand(cmd);
     }
 }
diff --git a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
index f3c8194..c9b5c96 100644
--- a/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
+++ b/tests/InputMethodStressTest/src/com/android/inputmethod/stresstest/ImeStressTestUtil.java
@@ -392,7 +392,7 @@
         public boolean showImeWithInputMethodManager() {
             boolean showResult =
                     getInputMethodManager()
-                            .showSoftInput(mEditText, InputMethodManager.SHOW_IMPLICIT);
+                            .showSoftInput(mEditText, 0 /* flags */);
             if (showResult) {
                 Log.i(TAG, "IMM#showSoftInput successfully");
             } else {
@@ -404,7 +404,8 @@
         /** Hide IME with InputMethodManager. */
         public boolean hideImeWithInputMethodManager() {
             boolean hideResult =
-                    getInputMethodManager().hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
+                    getInputMethodManager()
+                            .hideSoftInputFromWindow(mEditText.getWindowToken(), 0 /* flags */);
             if (hideResult) {
                 Log.i(TAG, "IMM#hideSoftInput successfully");
             } else {
diff --git a/tests/permission/OWNERS b/tests/permission/OWNERS
index 999ea0e..2fe78c5 100644
--- a/tests/permission/OWNERS
+++ b/tests/permission/OWNERS
@@ -1 +1,2 @@
+#Bug component: 137825
 include /core/java/android/permission/OWNERS
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
index e03d92ab..f1727b7 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/Constants.kt
@@ -30,6 +30,7 @@
 const val IINTERFACE_INTERFACE = "android.os.IInterface"
 
 const val AIDL_PERMISSION_HELPER_SUFFIX = "_enforcePermission"
+const val PERMISSION_PREFIX_LITERAL = "android.permission."
 
 /**
  * If a non java (e.g. c++) backend is enabled, the @EnforcePermission
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index 0a66cd5..3a95df9 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -95,7 +95,7 @@
             val v1 = ConstantEvaluator.evaluate(context, value1)
             val v2 = ConstantEvaluator.evaluate(context, value2)
             if (v1 != null && v2 != null) {
-                if (v1 != v2) {
+                if (v1 != v2 && !isOneShortPermissionOfOther(v1, v2)) {
                     return false
                 }
             } else {
@@ -107,7 +107,7 @@
                 for (j in children1.indices) {
                     val c1 = ConstantEvaluator.evaluate(context, children1[j])
                     val c2 = ConstantEvaluator.evaluate(context, children2[j])
-                    if (c1 != c2) {
+                    if (c1 != c2 && !isOneShortPermissionOfOther(c1, c2)) {
                         return false
                     }
                 }
@@ -116,6 +116,12 @@
         return true
     }
 
+    private fun isOneShortPermissionOfOther(
+        permission1: Any?,
+        permission2: Any?
+    ): Boolean = permission1 == (permission2 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL) ||
+            permission2 == (permission1 as? String)?.removePrefix(PERMISSION_PREFIX_LITERAL)
+
     private fun compareMethods(
         context: JavaContext,
         element: UElement,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
index 75b0073..b3dacbd 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/EnforcePermissionDetectorTest.kt
@@ -316,20 +316,55 @@
                 overrides the method Stub.testMethod which is annotated with @EnforcePermission. The same annotation must be used on Default.testMethod [MissingEnforcePermissionAnnotation]
                     public void testMethod() {}
                                 ~~~~~~~~~~
-                1 errors, 0 warnings 
+                1 errors, 0 warnings
                 """.addLineContinuation()
             )
     }
 
-    fun testDoesDetectIssuesShortStringsNotAllowed() {
+    fun testDoesNotDetectIssuesShortStringsAllowedInChildAndParent() {
         lint().files(java(
             """
             package test.pkg;
             import android.annotation.EnforcePermission;
             public class TestClass121 extends IFooMethod.Stub {
                 @Override
+                @EnforcePermission("READ_PHONE_STATE")
+                public void testMethod() {}
+                @Override
+                @EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
+                public void testMethodParentShortPermission() {}
+                @Override
                 @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
                 public void testMethodAnyLiteral() {}
+                @Override
+                @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
+                public void testMethodAnyLiteralParentsShortPermission() {}
+            }
+            """).indented(),
+            *stubs
+        )
+            .run()
+            .expectClean()
+    }
+
+    fun testDoesDetectIssuesWrongShortStringsInChildAndParent() {
+        lint().files(java(
+            """
+            package test.pkg;
+            import android.annotation.EnforcePermission;
+            public class TestClass121 extends IFooMethod.Stub {
+                @Override
+                @EnforcePermission("READ_WRONG_PHONE_STATE")
+                public void testMethod() {}
+                @Override
+                @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE)
+                public void testMethodParentShortPermission() {}
+                @Override
+                @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"})
+                public void testMethodAnyLiteral() {}
+                @Override
+                @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE})
+                public void testMethodAnyLiteralParentsShortPermission() {}
             }
             """).indented(),
             *stubs
@@ -337,14 +372,19 @@
             .run()
             .expect(
                 """
-                src/test/pkg/TestClass121.java:6: Error: The method \
-                TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}) \
-                which differs from the overridden method Stub.testMethodAnyLiteral: \
-                @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). \
-                The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
-                    public void testMethodAnyLiteral() {}
-                                ~~~~~~~~~~~~~~~~~~~~
-                1 errors, 0 warnings
+            src/test/pkg/TestClass121.java:6: Error: The method TestClass121.testMethod is annotated with @EnforcePermission("READ_WRONG_PHONE_STATE") which differs from the overridden method Stub.testMethod: @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+                public void testMethod() {}
+                            ~~~~~~~~~~
+            src/test/pkg/TestClass121.java:9: Error: The method TestClass121.testMethodParentShortPermission is annotated with @EnforcePermission(android.Manifest.permission.READ_WRONG_PHONE_STATE) which differs from the overridden method Stub.testMethodParentShortPermission: @android.annotation.EnforcePermission("READ_PHONE_STATE"). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+                public void testMethodParentShortPermission() {}
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+            src/test/pkg/TestClass121.java:12: Error: The method TestClass121.testMethodAnyLiteral is annotated with @EnforcePermission(anyOf={"WRONG_INTERNET", "READ_PHONE_STATE"}) which differs from the overridden method Stub.testMethodAnyLiteral: @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+                public void testMethodAnyLiteral() {}
+                            ~~~~~~~~~~~~~~~~~~~~
+            src/test/pkg/TestClass121.java:15: Error: The method TestClass121.testMethodAnyLiteralParentsShortPermission is annotated with @EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_WRONG_PHONE_STATE}) which differs from the overridden method Stub.testMethodAnyLiteralParentsShortPermission: @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"}). The same annotation must be used for both methods. [MismatchingEnforcePermissionAnnotation]
+                public void testMethodAnyLiteralParentsShortPermission() {}
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+            4 errors, 0 warnings
                 """.addLineContinuation()
             )
     }
@@ -360,12 +400,18 @@
             @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
             public void testMethod() {}
             @Override
+            @android.annotation.EnforcePermission("READ_PHONE_STATE")
+            public void testMethodParentShortPermission() {}
+            @Override
             @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
             public void testMethodAny() {}
             @Override
             @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
             public void testMethodAnyLiteral() {}
             @Override
+            @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+            public void testMethodAnyLiteralParentsShortPermission() {}
+            @Override
             @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
             public void testMethodAll() {}
             @Override
@@ -374,10 +420,14 @@
           }
           @android.annotation.EnforcePermission(android.Manifest.permission.READ_PHONE_STATE)
           public void testMethod();
+          @android.annotation.EnforcePermission("READ_PHONE_STATE")
+          public void testMethodParentShortPermission();
           @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
           public void testMethodAny() {}
           @android.annotation.EnforcePermission(anyOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
           public void testMethodAnyLiteral() {}
+          @android.annotation.EnforcePermission(anyOf={"INTERNET", "READ_PHONE_STATE"})
+          public void testMethodAnyLiteralParentsShortPermission() {}
           @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, android.Manifest.permission.READ_PHONE_STATE})
           public void testMethodAll() {}
           @android.annotation.EnforcePermission(allOf={android.Manifest.permission.INTERNET, "android.permission.READ_PHONE_STATE"})
@@ -404,6 +454,7 @@
         package android.Manifest;
         class permission {
           public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+          public static final String READ_WRONG_PHONE_STATE = "android.permission.READ_WRONG_PHONE_STATE";
           public static final String NFC = "android.permission.NFC";
           public static final String INTERNET = "android.permission.INTERNET";
         }