Merge "Fix some issues reading xml"
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index dd951b4..c1d1518 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -224,6 +224,7 @@
     field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES";
     field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO";
     field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
+    field public static final String READ_COMMUNAL_STATE = "android.permission.READ_COMMUNAL_STATE";
     field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS";
     field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG";
     field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE";
@@ -393,6 +394,7 @@
     field public static final int config_helpPackageNameValue = 17039388; // 0x104001c
     field public static final int config_systemActivityRecognizer = 17039416; // 0x1040038
     field public static final int config_systemAmbientAudioIntelligence = 17039411; // 0x1040033
+    field public static final int config_systemAppProtectionService;
     field public static final int config_systemAudioIntelligence = 17039412; // 0x1040034
     field public static final int config_systemAutomotiveCluster = 17039400; // 0x1040028
     field public static final int config_systemAutomotiveProjection = 17039401; // 0x1040029
@@ -1309,6 +1311,20 @@
 
 }
 
+package android.app.communal {
+
+  public final class CommunalManager {
+    method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void addCommunalModeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.communal.CommunalManager.CommunalModeListener);
+    method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public boolean isCommunalMode();
+    method @RequiresPermission(android.Manifest.permission.READ_COMMUNAL_STATE) public void removeCommunalModeListener(@NonNull android.app.communal.CommunalManager.CommunalModeListener);
+  }
+
+  @java.lang.FunctionalInterface public static interface CommunalManager.CommunalModeListener {
+    method public void onCommunalModeChanged(boolean);
+  }
+
+}
+
 package android.app.compat {
 
   public final class CompatChanges {
@@ -2491,6 +2507,7 @@
     field public static final String BATTERY_STATS_SERVICE = "batterystats";
     field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
     field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
+    field public static final String COMMUNAL_SERVICE = "communal";
     field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
     field public static final String ETHERNET_SERVICE = "ethernet";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index aa791aa..bf06db0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -42,6 +42,7 @@
     field public static final String TEST_BIOMETRIC = "android.permission.TEST_BIOMETRIC";
     field public static final String TEST_MANAGE_ROLLBACKS = "android.permission.TEST_MANAGE_ROLLBACKS";
     field public static final String UPGRADE_RUNTIME_PERMISSIONS = "android.permission.UPGRADE_RUNTIME_PERMISSIONS";
+    field public static final String WRITE_COMMUNAL_STATE = "android.permission.WRITE_COMMUNAL_STATE";
     field public static final String WRITE_DEVICE_CONFIG = "android.permission.WRITE_DEVICE_CONFIG";
     field @Deprecated public static final String WRITE_MEDIA_STORAGE = "android.permission.WRITE_MEDIA_STORAGE";
     field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
@@ -608,6 +609,14 @@
 
 }
 
+package android.app.communal {
+
+  public final class CommunalManager {
+    method @RequiresPermission(android.Manifest.permission.WRITE_COMMUNAL_STATE) public void setCommunalViewShowing(boolean);
+  }
+
+}
+
 package android.app.contentsuggestions {
 
   public final class ContentSuggestionsManager {
@@ -819,6 +828,7 @@
     method public void holdLock(android.os.IBinder, int);
     method @RequiresPermission(android.Manifest.permission.KEEP_UNINSTALLED_PACKAGES) public void setKeepUninstalledPackages(@NonNull java.util.List<java.lang.String>);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
+    field public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
     field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
     field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 089c269..81e6ae4 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1513,7 +1513,7 @@
                     }
                 });
 
-        registerService(Context.COMMUNAL_MANAGER_SERVICE, CommunalManager.class,
+        registerService(Context.COMMUNAL_SERVICE, CommunalManager.class,
                 new CachedServiceFetcher<CommunalManager>() {
                     @Override
                     public CommunalManager createService(ContextImpl ctx) {
@@ -1522,7 +1522,7 @@
                             return null;
                         }
                         IBinder iBinder =
-                                ServiceManager.getService(Context.COMMUNAL_MANAGER_SERVICE);
+                                ServiceManager.getService(Context.COMMUNAL_SERVICE);
                         return iBinder != null ? new CommunalManager(
                                 ICommunalManager.Stub.asInterface(iBinder)) : null;
                     }
diff --git a/core/java/android/app/communal/CommunalManager.java b/core/java/android/app/communal/CommunalManager.java
index 60730ad..22f07693 100644
--- a/core/java/android/app/communal/CommunalManager.java
+++ b/core/java/android/app/communal/CommunalManager.java
@@ -17,15 +17,21 @@
 package android.app.communal;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.Disabled;
 import android.compat.annotation.Overridable;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import java.util.concurrent.Executor;
 
 /**
  * System private class for talking with the
@@ -33,10 +39,12 @@
  *
  * @hide
  */
-@SystemService(Context.COMMUNAL_MANAGER_SERVICE)
+@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
+@SystemService(Context.COMMUNAL_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_COMMUNAL_MODE)
 public final class CommunalManager {
     private final ICommunalManager mService;
+    private final ArrayMap<CommunalModeListener, ICommunalModeListener> mCommunalModeListeners;
 
     /**
      * This change id is used to annotate packages which can run in communal mode by default,
@@ -59,15 +67,20 @@
     @Disabled
     public static final long ALLOW_COMMUNAL_MODE_WITH_USER_CONSENT = 200324021L;
 
+    /** @hide */
     public CommunalManager(ICommunalManager service) {
         mService = service;
+        mCommunalModeListeners = new ArrayMap<CommunalModeListener, ICommunalModeListener>();
     }
 
     /**
      * Updates whether or not the communal view is currently showing over the lockscreen.
      *
      * @param isShowing Whether communal view is showing.
+     *
+     * @hide
      */
+    @TestApi
     @RequiresPermission(Manifest.permission.WRITE_COMMUNAL_STATE)
     public void setCommunalViewShowing(boolean isShowing) {
         try {
@@ -76,4 +89,72 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Checks whether or not the communal view is currently showing over the lockscreen.
+     */
+    @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+    public boolean isCommunalMode() {
+        try {
+            return mService.isCommunalMode();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Listener for communal state changes.
+     */
+    @FunctionalInterface
+    public interface CommunalModeListener {
+        /**
+         * Callback function that executes when the communal state changes.
+         */
+        void onCommunalModeChanged(boolean isCommunalMode);
+    }
+
+    /**
+     * Registers a callback to execute when the communal state changes.
+     *
+     * @param listener The listener to add to receive communal state changes.
+     * @param executor {@link Executor} to dispatch to. To dispatch the callback to the main
+     *                 thread of your application, use
+     *                 {@link android.content.Context#getMainExecutor()}.
+     */
+    @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+    public void addCommunalModeListener(@NonNull Executor executor,
+            @NonNull CommunalModeListener listener) {
+        synchronized (mCommunalModeListeners) {
+            try {
+                ICommunalModeListener iListener = new ICommunalModeListener.Stub() {
+                    @Override
+                    public void onCommunalModeChanged(boolean isCommunalMode) {
+                        executor.execute(() -> listener.onCommunalModeChanged(isCommunalMode));
+                    }
+                };
+                mService.addCommunalModeListener(iListener);
+                mCommunalModeListeners.put(listener, iListener);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+    }
+
+    /**
+     * Unregisters a callback that executes when communal state changes.
+     */
+    @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+    public void removeCommunalModeListener(@NonNull CommunalModeListener listener) {
+        synchronized (mCommunalModeListeners) {
+            ICommunalModeListener iListener = mCommunalModeListeners.get(listener);
+            if (iListener != null) {
+                try {
+                    mService.removeCommunalModeListener(iListener);
+                    mCommunalModeListeners.remove(listener);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/communal/ICommunalManager.aidl b/core/java/android/app/communal/ICommunalManager.aidl
index 02e8a65..869891e 100644
--- a/core/java/android/app/communal/ICommunalManager.aidl
+++ b/core/java/android/app/communal/ICommunalManager.aidl
@@ -16,12 +16,17 @@
 
 package android.app.communal;
 
+import android.app.communal.ICommunalModeListener;
+
 /**
  * System private API for talking with the communal manager service that handles communal mode
  * state.
  *
  * @hide
  */
-oneway interface ICommunalManager {
-    void setCommunalViewShowing(boolean isShowing);
+interface ICommunalManager {
+    oneway void setCommunalViewShowing(boolean isShowing);
+    boolean isCommunalMode();
+    void addCommunalModeListener(in ICommunalModeListener listener);
+    void removeCommunalModeListener(in ICommunalModeListener listener);
 }
\ No newline at end of file
diff --git a/core/java/android/app/communal/ICommunalModeListener.aidl b/core/java/android/app/communal/ICommunalModeListener.aidl
new file mode 100644
index 0000000..006e782
--- /dev/null
+++ b/core/java/android/app/communal/ICommunalModeListener.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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 android.app.communal;
+
+/**
+ * System private API to be notified about communal mode changes.
+ *
+ * @hide
+ */
+oneway interface ICommunalModeListener {
+    void onCommunalModeChanged(boolean isCommunalMode);
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/TransportBlock.java b/core/java/android/bluetooth/le/TransportBlock.java
index b388bed..18bad9c 100644
--- a/core/java/android/bluetooth/le/TransportBlock.java
+++ b/core/java/android/bluetooth/le/TransportBlock.java
@@ -24,6 +24,7 @@
 
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
 
 /**
  * Wrapper for Transport Discovery Data Transport Blocks.
@@ -59,8 +60,12 @@
         mOrgId = in.readInt();
         mTdsFlags = in.readInt();
         mTransportDataLength = in.readInt();
-        mTransportData = new byte[mTransportDataLength];
-        in.readByteArray(mTransportData);
+        if (mTransportDataLength > 0) {
+            mTransportData = new byte[mTransportDataLength];
+            in.readByteArray(mTransportData);
+        } else {
+            mTransportData = null;
+        }
     }
 
     @Override
@@ -68,7 +73,9 @@
         dest.writeInt(mOrgId);
         dest.writeInt(mTdsFlags);
         dest.writeInt(mTransportDataLength);
-        dest.writeByteArray(mTransportData);
+        if (mTransportData != null) {
+            dest.writeByteArray(mTransportData);
+        }
     }
 
     /**
@@ -79,6 +86,21 @@
         return 0;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        TransportBlock other = (TransportBlock) obj;
+        return Arrays.equals(toByteArray(), other.toByteArray());
+    }
+
     public static final @NonNull Creator<TransportBlock> CREATOR = new Creator<TransportBlock>() {
         @Override
         public TransportBlock createFromParcel(Parcel in) {
diff --git a/core/java/android/bluetooth/le/TransportDiscoveryData.java b/core/java/android/bluetooth/le/TransportDiscoveryData.java
index c8e97f9..2b52f19 100644
--- a/core/java/android/bluetooth/le/TransportDiscoveryData.java
+++ b/core/java/android/bluetooth/le/TransportDiscoveryData.java
@@ -26,6 +26,7 @@
 import java.nio.BufferUnderflowException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -96,6 +97,21 @@
         return 0;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        TransportDiscoveryData other = (TransportDiscoveryData) obj;
+        return Arrays.equals(toByteArray(), other.toByteArray());
+    }
+
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeInt(mTransportDataType);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 73740d2c..543239b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5862,13 +5862,14 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link android.app.CommunalManager} for interacting with the global system state.
+     * {@link android.app.communal.CommunalManager} for interacting with the global system state.
      *
      * @see #getSystemService(String)
-     * @see android.app.CommunalManager
+     * @see android.app.communal.CommunalManager
      * @hide
      */
-    public static final String COMMUNAL_MANAGER_SERVICE = "communal_manager";
+    @SystemApi
+    public static final String COMMUNAL_SERVICE = "communal";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bea536e..1c35b47 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3981,6 +3981,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.FEATURE)
+    @TestApi
     public static final String FEATURE_COMMUNAL_MODE = "android.software.communal_mode";
 
     /** @hide */
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c76245b..75d5ecf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -533,10 +533,11 @@
 
     boolean mReportNextDraw;
     /**
-     * Set if the reportDraw was requested from WM. If just a local report draw was invoked, there's
-     * no need to report back to system server and can just apply immediately on the client.
+     * Set whether the draw should use blast sync. This is in case the draw is canceled,
+     * but will be rescheduled. We still want the next draw to be sync.
      */
-    boolean mReportDrawToWm;
+    boolean mNextDrawUseBlastSync;
+
     boolean mFullRedrawNeeded;
     boolean mNewSurfaceNeeded;
     boolean mForceNextWindowRelayout;
@@ -2761,7 +2762,7 @@
             }
         }
         final boolean wasReportNextDraw = mReportNextDraw;
-        boolean useBlastSync = false;
+        boolean useBlastSync = mNextDrawUseBlastSync;
 
         if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
                 || mForceNextWindowRelayout) {
@@ -3292,9 +3293,11 @@
                 mPendingTransitions.clear();
             }
             performDraw(useBlastSync);
+            mNextDrawUseBlastSync = false;
         } else {
             if (isViewVisible) {
                 // Try again
+                mNextDrawUseBlastSync = useBlastSync;
                 scheduleTraversals();
             } else {
                 if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -3984,17 +3987,8 @@
         }
         mDrawsNeededToReport = 0;
 
-        if (!mReportDrawToWm) {
-            if (DEBUG_BLAST) {
-                Log.d(mTag, "No need to report finishDrawing. Apply immediately");
-            }
-            mSurfaceChangedTransaction.apply();
-            return;
-        }
-
         try {
             mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction);
-            mReportDrawToWm = false;
         } catch (RemoteException e) {
             Log.e(mTag, "Unable to report draw finished", e);
             mSurfaceChangedTransaction.apply();
@@ -9604,7 +9598,6 @@
         if (mReportNextDraw == false) {
             drawPending();
         }
-        mReportDrawToWm = true;
         mReportNextDraw = true;
     }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6e2c807..7018df2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2798,7 +2798,7 @@
     <!-- @SystemApi @hide Allows an application to set the profile owners and the device owner.
          This permission is not available to third party applications.-->
     <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
-        android:protectionLevel="signature|role"
+        android:protectionLevel="signature|role|setup"
         android:label="@string/permlab_manageProfileAndDeviceOwners"
         android:description="@string/permdesc_manageProfileAndDeviceOwners" />
 
@@ -5531,10 +5531,18 @@
 
     <!-- Allows an application to interact with the currently active
         {@link com.android.server.communal.CommunalManagerService}.
-        @hide -->
+        @hide
+        @TestApi -->
     <permission android:name="android.permission.WRITE_COMMUNAL_STATE"
                 android:protectionLevel="signature" />
 
+    <!-- Allows an application to view information from the currently active
+         {@link com.android.server.communal.CommunalManagerService}.
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.READ_COMMUNAL_STATE"
+                android:protectionLevel="signature|privileged"/>
+
     <!-- Allows the holder to manage whether the system can bind to services
          provided by instant apps. This permission is intended to protect
          test/development fucntionality and should be used only in such cases.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ccde348..480c679 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2123,6 +2123,8 @@
     <string name="config_systemTelevisionRemoteService" translatable="false">@string/config_tvRemoteServicePackage</string>
     <!-- The name of the package that will hold the device management role -->
     <string name="config_deviceManager" translatable="false"></string>
+    <!-- The name of the package that will hold the app protection service role. -->
+    <string name="config_systemAppProtectionService" translatable="false"></string>
 
     <!-- The name of the package that will be allowed to change its components' label/icon. -->
     <string name="config_overrideComponentUiPackage" translatable="false">com.android.stk</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ca80def..1aa3ac2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3267,6 +3267,8 @@
     <public name="config_systemSupervision" />
     <!-- @hide @SystemApi -->
     <public name="config_deviceManager" />
+    <!-- @hide @SystemApi -->
+    <public name="config_systemAppProtectionService" />
   </staging-public-group>
 
   <staging-public-group type="dimen" first-id="0x01db0000">
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 60cb9d3..81db63e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -519,6 +519,9 @@
         <permission name="android.permission.LOCK_DEVICE" />
         <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
         <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
+        <!-- Permission required for CTS test - CommunalManagerTest -->
+        <permission name="android.permission.WRITE_COMMUNAL_STATE" />
+        <permission name="android.permission.READ_COMMUNAL_STATE" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
index 9a41cb4..fa173072 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/RequiresPermissionChecker.java
@@ -189,7 +189,10 @@
         if (!actualPerm.containsAll(expectedPerm)) {
             return buildDescription(tree)
                     .setMessage("Method " + method.name.toString() + "() annotated " + expectedPerm
-                            + " but too wide; only invokes methods requiring " + actualPerm)
+                            + " but too wide; only invokes methods requiring " + actualPerm
+                            + "\n  If calling an AIDL interface, it can be annotated by adding:"
+                            + "\n  @JavaPassthrough(annotation=\""
+                            + "@android.annotation.RequiresPermission(...)\")")
                     .build();
         }
 
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index ca3ec3c..688d116 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -112,6 +112,8 @@
     <!--  [CHAR LIMIT=none] -->
     <string name="uninstall_application_text_user">Do you want to uninstall this app for the user <xliff:g id="username">%1$s</xliff:g>?</string>
     <!--  [CHAR LIMIT=none] -->
+    <string name="uninstall_application_text_current_user_work_profile">Do you want to uninstall this app from your work profile?</string>
+    <!--  [CHAR LIMIT=none] -->
     <string name="uninstall_update_text">Replace this app with the factory version? All data will be removed.</string>
     <!--  [CHAR LIMIT=none] -->
     <string name="uninstall_update_text_multiuser">Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
index 99f6a92..36294ac 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -31,6 +31,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
@@ -125,6 +126,7 @@
 
         final boolean isUpdate =
                 ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+        final UserHandle myUserHandle = Process.myUserHandle();
         UserManager userManager = UserManager.get(getActivity());
         if (isUpdate) {
             if (isSingleUser(userManager)) {
@@ -135,10 +137,17 @@
         } else {
             if (dialogInfo.allUsers && !isSingleUser(userManager)) {
                 messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
-            } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+            } else if (!dialogInfo.user.equals(myUserHandle)) {
                 UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
-                messageBuilder.append(
-                        getString(R.string.uninstall_application_text_user, userInfo.name));
+                if (userInfo.isManagedProfile()
+                        && userInfo.profileGroupId == myUserHandle.getIdentifier()) {
+                    messageBuilder.append(
+                            getString(R.string.uninstall_application_text_current_user_work_profile,
+                                    userInfo.name));
+                } else {
+                    messageBuilder.append(
+                            getString(R.string.uninstall_application_text_user, userInfo.name));
+                }
             } else {
                 messageBuilder.append(getString(R.string.uninstall_application_text));
             }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
index 21d25f5..2d241ca 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -21,7 +21,10 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.os.Bundle;
+import android.os.Process;
+import android.os.UserHandle;
 import android.os.UserManager;
+
 import androidx.leanback.app.GuidedStepFragment;
 import androidx.leanback.widget.GuidanceStylist;
 import androidx.leanback.widget.GuidedAction;
@@ -59,6 +62,7 @@
 
         final boolean isUpdate =
                 ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+        final UserHandle myUserHandle = Process.myUserHandle();
         UserManager userManager = UserManager.get(getActivity());
         if (isUpdate) {
             if (isSingleUser(userManager)) {
@@ -69,10 +73,17 @@
         } else {
             if (dialogInfo.allUsers && !isSingleUser(userManager)) {
                 messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
-            } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+            } else if (!dialogInfo.user.equals(myUserHandle)) {
                 UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
-                messageBuilder.append(
-                        getString(R.string.uninstall_application_text_user, userInfo.name));
+                if (userInfo.isManagedProfile()
+                        && userInfo.profileGroupId == myUserHandle.getIdentifier()) {
+                    messageBuilder.append(
+                            getString(R.string.uninstall_application_text_current_user_work_profile,
+                                    userInfo.name));
+                } else {
+                    messageBuilder.append(
+                            getString(R.string.uninstall_application_text_user, userInfo.name));
+                }
             } else {
                 messageBuilder.append(getString(R.string.uninstall_application_text));
             }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 262cf53..e5b5285 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -606,6 +606,10 @@
     <!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
     <uses-permission android:name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
 
+    <!-- Permission required for CTS test - CommunalManagerTest -->
+    <uses-permission android:name="android.permission.WRITE_COMMUNAL_STATE" />
+    <uses-permission android:name="android.permission.READ_COMMUNAL_STATE" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
new file mode 100644
index 0000000..177f695
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_header_bg.xml
@@ -0,0 +1,36 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+            xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+            android:paddingMode="stack"
+            android:paddingStart="44dp"
+            android:paddingEnd="44dp"
+            android:paddingLeft="0dp"
+            android:paddingRight="0dp">
+    <item>
+        <shape android:shape="rectangle">
+          <solid android:color="?androidprv:attr/colorSurface" />
+            <corners android:radius="@dimen/keyguard_user_switcher_corner" />
+        </shape>
+    </item>
+    <item
+        android:drawable="@drawable/ic_ksh_key_down"
+        android:gravity="end|center_vertical"
+        android:width="32dp"
+        android:height="32dp"
+        android:end="12dp" />
+</layer-list>
diff --git a/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
new file mode 100644
index 0000000..96a2d15
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/keyguard_user_switcher_popup_bg.xml
@@ -0,0 +1,22 @@
+<?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"
+       xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+       android:shape="rectangle">
+    <solid android:color="?androidprv:attr/colorSurface" />
+    <corners android:radius="@dimen/keyguard_user_switcher_popup_corner" />
+</shape>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
new file mode 100644
index 0000000..a2b8bf6
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -0,0 +1,55 @@
+<?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.
+*/
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/keyguard_bouncer_user_switcher"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:clipToPadding="false"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:paddingTop="12dp"
+    android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
+                                                  from this view when bouncer is shown -->
+
+  <ImageView
+      android:id="@+id/user_icon"
+      android:layout_width="@dimen/keyguard_user_switcher_icon_size"
+      android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
+
+    <!-- need to keep this outer view in order to have a correctly sized anchor
+         for the dropdown menu, as well as dropdown background in the right place -->
+    <LinearLayout
+        android:id="@+id/user_switcher_anchor"
+        android:orientation="horizontal"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="32dp"
+        android:minHeight="48dp">
+      <TextView
+          style="@style/Keyguard.UserSwitcher.Spinner.Header"
+          android:clickable="false"
+          android:id="@+id/user_switcher_header"
+          android:layout_width="@dimen/keyguard_user_switcher_width"
+          android:layout_height="wrap_content" />
+    </LinearLayout>>
+
+</LinearLayout>
+
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
new file mode 100644
index 0000000..b08e1ff
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher_item.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ 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.
+  -->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/Keyguard.UserSwitcher.Spinner.Item"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="start"
+    android:paddingStart="@dimen/control_menu_horizontal_padding"
+    android:paddingEnd="@dimen/control_menu_horizontal_padding"
+    android:textDirection="locale"/>
+
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index a946318..94566c7 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -49,8 +49,6 @@
             android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom"
             androidprv:layout_constraintEnd_toEndOf="parent"
             androidprv:layout_constraintStart_toStartOf="parent"
-
-            androidprv:layout_constraintTop_toTopOf="parent"
             androidprv:layout_constraintBottom_toTopOf="@id/key1"
             androidprv:layout_constraintVertical_bias="0.0">
 
diff --git a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
index 4daa648..54bb1fc 100644
--- a/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
+++ b/packages/SystemUI/res-keyguard/values-sw720dp/bools.xml
@@ -18,7 +18,7 @@
 <resources>
     <!-- Allows PIN/Pattern to be drawn on one side of a display, and for the user to
          switch sides -->
-    <bool name="can_use_one_handed_bouncer">true</bool>
+    <bool name="can_use_one_handed_bouncer">false</bool>
 
     <!-- Will display the bouncer on one side of the display, and the current user icon and
          user switcher on the other side -->
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 89dd741..9533040 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -106,4 +106,13 @@
          opacity is zero), but this controls how much motion will actually be applied to it while
          animating. Larger values will cause it to move "faster" while fading out/in. -->
     <dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
+
+
+    <dimen name="keyguard_user_switcher_header_text_size">24sp</dimen>
+    <dimen name="keyguard_user_switcher_item_text_size">18sp</dimen>
+    <dimen name="keyguard_user_switcher_width">300dp</dimen>
+    <dimen name="keyguard_user_switcher_icon_size">250dp</dimen>
+    <dimen name="keyguard_user_switcher_corner">32dp</dimen>
+    <dimen name="keyguard_user_switcher_popup_corner">24dp</dimen>
+    <dimen name="keyguard_user_switcher_item_padding_vertical">15dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res-keyguard/values/styles.xml b/packages/SystemUI/res-keyguard/values/styles.xml
index b0bdc72..a7b2b47 100644
--- a/packages/SystemUI/res-keyguard/values/styles.xml
+++ b/packages/SystemUI/res-keyguard/values/styles.xml
@@ -139,4 +139,23 @@
     <style name="TextAppearance.Keyguard.BottomArea.Button">
         <item name="android:shadowRadius">0</item>
     </style>
+
+    <style name="Keyguard.UserSwitcher.Spinner" parent="@android:style/Widget.DeviceDefault.TextView">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:paddingTop">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+        <item name="android:paddingBottom">@dimen/keyguard_user_switcher_item_padding_vertical</item>
+    </style>
+
+    <style name="Keyguard.UserSwitcher.Spinner.Header">
+        <item name="android:background">@drawable/keyguard_user_switcher_header_bg</item>
+        <item name="android:textSize">@dimen/keyguard_user_switcher_header_text_size</item>
+    </style>
+
+    <style name="Keyguard.UserSwitcher.Spinner.Item">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textSize">@dimen/keyguard_user_switcher_item_text_size</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index 40190c1..7eae729 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -48,10 +48,6 @@
 
     abstract CharSequence getTitle();
 
-    void animateForIme(float interpolatedFraction, boolean appearingAnim) {
-        return;
-    }
-
     boolean disallowInterceptTouch(MotionEvent event) {
         return false;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 3a3d308..bc366ab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -156,8 +156,7 @@
         setAlpha(0f);
         animate()
             .alpha(1f)
-            .setDuration(500)
-            .setStartDelay(300)
+            .setDuration(300)
             .start();
 
         setTranslationY(0f);
@@ -219,15 +218,6 @@
         return true;
     }
 
-
-    @Override
-    public void animateForIme(float interpolatedFraction, boolean appearingAnim) {
-        animate().cancel();
-        setAlpha(appearingAnim
-                ? Math.max(interpolatedFraction, getAlpha())
-                : 1 - interpolatedFraction);
-    }
-
     @Override
     public CharSequence getTitle() {
         return getResources().getString(
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 172c7f6..95567ec 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -23,17 +23,23 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.TypedValue;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -44,7 +50,10 @@
 import android.view.WindowManager;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.widget.AdapterView;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
@@ -56,12 +65,17 @@
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.UserIcons;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.animation.Interpolators;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter;
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import java.util.ArrayList;
@@ -110,6 +124,8 @@
     @VisibleForTesting
     KeyguardSecurityViewFlipper mSecurityViewFlipper;
     private GlobalSettings mGlobalSettings;
+    private FalsingCollector mFalsingCollector;
+    private UserSwitcherController mUserSwitcherController;
     private AlertDialog mAlertDialog;
     private boolean mSwipeUpToRetry;
 
@@ -124,7 +140,7 @@
     private float mStartTouchY = -1;
     private boolean mDisappearAnimRunning;
     private SwipeListener mSwipeListener;
-    private ModeLogic mModeLogic = new DefaultModeLogic();
+    private ViewMode mViewMode = new DefaultViewMode();
     private @Mode int mCurrentMode = MODE_DEFAULT;
 
     private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -173,8 +189,11 @@
                                 interpolatedFraction);
                         translationY += paddingBottom;
                     }
-                    mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction,
-                            !mDisappearAnimRunning);
+
+                    float alpha = mDisappearAnimRunning
+                            ? 1 - interpolatedFraction
+                            : Math.max(interpolatedFraction, getAlpha());
+                    updateChildren(translationY, alpha);
 
                     return windowInsets;
                 }
@@ -183,12 +202,19 @@
                 public void onEnd(WindowInsetsAnimation animation) {
                     if (!mDisappearAnimRunning) {
                         endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_APPEAR);
-                        mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f,
-                                true /* appearingAnim */);
+                        updateChildren(0 /* translationY */, 1f /* alpha */);
                     } else {
                         endJankInstrument(InteractionJankMonitor.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR);
                     }
                 }
+
+                private void updateChildren(int translationY, float alpha) {
+                    for (int i = 0; i < KeyguardSecurityContainer.this.getChildCount(); ++i) {
+                        View child = KeyguardSecurityContainer.this.getChildAt(i);
+                        child.setTranslationY(translationY);
+                        child.setAlpha(alpha);
+                    }
+                }
             };
 
     // Used to notify the container when something interesting happens.
@@ -270,9 +296,12 @@
     void onResume(SecurityMode securityMode, boolean faceAuthEnabled) {
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback);
         updateBiometricRetry(securityMode, faceAuthEnabled);
+
+        setupViewMode();
     }
 
-    void initMode(@Mode int mode, GlobalSettings globalSettings) {
+    void initMode(@Mode int mode, GlobalSettings globalSettings, FalsingCollector falsingCollector,
+            UserSwitcherController userSwitcherController) {
         if (mCurrentMode == mode) return;
         Log.i(TAG, "Switching mode from " + modeToString(mCurrentMode) + " to "
                 + modeToString(mode));
@@ -280,16 +309,18 @@
 
         switch (mode) {
             case MODE_ONE_HANDED:
-                mModeLogic = new OneHandedModeLogic();
+                mViewMode = new OneHandedViewMode();
                 break;
             case MODE_USER_SWITCHER:
-                mModeLogic = new UserSwitcherModeLogic();
+                mViewMode = new UserSwitcherViewMode();
                 break;
             default:
-                mModeLogic = new DefaultModeLogic();
+                mViewMode = new DefaultViewMode();
         }
         mGlobalSettings = globalSettings;
-        finishSetup();
+        mFalsingCollector = falsingCollector;
+        mUserSwitcherController = userSwitcherController;
+        setupViewMode();
     }
 
     private String modeToString(@Mode int mode) {
@@ -305,10 +336,14 @@
         }
     }
 
-    private void finishSetup() {
-        if (mSecurityViewFlipper == null || mGlobalSettings == null) return;
+    private void setupViewMode() {
+        if (mSecurityViewFlipper == null || mGlobalSettings == null
+                || mFalsingCollector == null || mUserSwitcherController == null) {
+            return;
+        }
 
-        mModeLogic.init(this, mGlobalSettings, mSecurityViewFlipper);
+        mViewMode.init(this, mGlobalSettings, mSecurityViewFlipper, mFalsingCollector,
+                mUserSwitcherController);
     }
 
     @Mode int getMode() {
@@ -321,13 +356,13 @@
      * that the user last interacted with.
      */
     void updatePositionByTouchX(float x) {
-        mModeLogic.updatePositionByTouchX(x);
+        mViewMode.updatePositionByTouchX(x);
     }
 
     /** Returns whether the inner SecurityViewFlipper is left-aligned when in one-handed mode. */
     public boolean isOneHandedModeLeftAligned() {
         return mCurrentMode == MODE_ONE_HANDED
-                && ((OneHandedModeLogic) mModeLogic).isLeftAligned();
+                && ((OneHandedViewMode) mViewMode).isLeftAligned();
     }
 
     public void onPause() {
@@ -336,6 +371,7 @@
             mAlertDialog = null;
         }
         mSecurityViewFlipper.setWindowInsetsAnimationCallback(null);
+        mViewMode.reset();
     }
 
     @Override
@@ -428,7 +464,7 @@
                 }
             } else {
                 if (!mIsDragging) {
-                    mModeLogic.handleTap(event);
+                    mViewMode.handleTap(event);
                 }
             }
         }
@@ -453,8 +489,19 @@
                 .animateToFinalPosition(0);
     }
 
+    /**
+     * Runs after a succsssful authentication only
+     */
     public void startDisappearAnimation(SecurityMode securitySelection) {
         mDisappearAnimRunning = true;
+        mViewMode.startDisappearAnimation(securitySelection);
+    }
+
+    /**
+     * This will run when the bouncer shows in all cases except when the user drags the bouncer up.
+     */
+    public void startAppearAnimation(SecurityMode securityMode) {
+        mViewMode.startAppearAnimation(securityMode);
     }
 
     private void beginJankInstrument(int cuj) {
@@ -490,8 +537,6 @@
     public void onFinishInflate() {
         super.onFinishInflate();
         mSecurityViewFlipper = findViewById(R.id.view_flipper);
-
-        finishSetup();
     }
 
     @Override
@@ -562,10 +607,7 @@
         for (int i = 0; i < getChildCount(); i++) {
             final View view = getChildAt(i);
             if (view.getVisibility() != GONE) {
-                int updatedWidthMeasureSpec = widthMeasureSpec;
-                if (view == mSecurityViewFlipper) {
-                    updatedWidthMeasureSpec = mModeLogic.getChildWidthMeasureSpec(widthMeasureSpec);
-                }
+                int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
                 measureChildWithMargins(view, updatedWidthMeasureSpec, 0, heightMeasureSpec, 0);
 
                 final LayoutParams lp = (LayoutParams) view.getLayoutParams();
@@ -595,7 +637,13 @@
 
         // After a layout pass, we need to re-place the inner bouncer, as our bounds may have
         // changed.
-        mModeLogic.updateSecurityViewLocation();
+        mViewMode.updateSecurityViewLocation();
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration config) {
+        super.onConfigurationChanged(config);
+        mViewMode.updateSecurityViewLocation();
     }
 
     void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
@@ -643,10 +691,12 @@
     /**
      * Enscapsulates the differences between bouncer modes for the container.
      */
-    private interface ModeLogic {
+    interface ViewMode {
 
-        default void init(ViewGroup v, GlobalSettings globalSettings,
-                KeyguardSecurityViewFlipper viewFlipper) {};
+        default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+                @NonNull KeyguardSecurityViewFlipper viewFlipper,
+                @NonNull FalsingCollector falsingCollector,
+                @NonNull UserSwitcherController userSwitcherController) {};
 
         /** Reinitialize the location */
         default void updateSecurityViewLocation() {};
@@ -657,19 +707,33 @@
         /** A tap on the container, outside of the ViewFlipper */
         default void handleTap(MotionEvent event) {};
 
+        /** Called when the view needs to reset or hides */
+        default void reset() {};
+
+        /** On a successful auth, optionally handle how the view disappears */
+        default void startDisappearAnimation(SecurityMode securityMode) {};
+
+        /** On notif tap, this animation will run */
+        default void startAppearAnimation(SecurityMode securityMode) {};
+
         /** Override to alter the width measure spec to perhaps limit the ViewFlipper size */
         default int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
             return parentWidthMeasureSpec;
         }
     }
 
-    private static class DefaultModeLogic implements ModeLogic {
+    /**
+     * Default bouncer is centered within the space
+     */
+    static class DefaultViewMode implements ViewMode {
         private ViewGroup mView;
         private KeyguardSecurityViewFlipper mViewFlipper;
 
         @Override
-        public void init(ViewGroup v, GlobalSettings globalSettings,
-                KeyguardSecurityViewFlipper viewFlipper) {
+        public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+                @NonNull KeyguardSecurityViewFlipper viewFlipper,
+                @NonNull FalsingCollector falsingCollector,
+                @NonNull UserSwitcherController userSwitcherController) {
             mView = v;
             mViewFlipper = viewFlipper;
 
@@ -682,7 +746,6 @@
                     (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
             lp.gravity = Gravity.CENTER_HORIZONTAL;
             mViewFlipper.setLayoutParams(lp);
-
             mViewFlipper.setTranslationX(0);
         }
     }
@@ -691,13 +754,171 @@
      * User switcher mode will display both the current user icon as well as
      * a user switcher, in both portrait and landscape modes.
      */
-    private static class UserSwitcherModeLogic implements ModeLogic {
+    static class UserSwitcherViewMode implements ViewMode {
         private ViewGroup mView;
+        private ViewGroup mUserSwitcherViewGroup;
+        private KeyguardSecurityViewFlipper mViewFlipper;
+        private ImageView mUserIconView;
+        private TextView mUserSwitcher;
+        private FalsingCollector mFalsingCollector;
+        private UserSwitcherController mUserSwitcherController;
+        private KeyguardUserSwitcherPopupMenu mPopup;
 
         @Override
-        public void init(ViewGroup v, GlobalSettings globalSettings,
-                KeyguardSecurityViewFlipper viewFlipper) {
+        public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+                @NonNull KeyguardSecurityViewFlipper viewFlipper,
+                @NonNull FalsingCollector falsingCollector,
+                @NonNull UserSwitcherController userSwitcherController) {
             mView = v;
+            mViewFlipper = viewFlipper;
+            mFalsingCollector = falsingCollector;
+            mUserSwitcherController = userSwitcherController;
+
+            if (mUserSwitcherViewGroup == null) {
+                LayoutInflater.from(v.getContext()).inflate(
+                        R.layout.keyguard_bouncer_user_switcher,
+                        mView,
+                        true);
+                mUserSwitcherViewGroup =  mView.findViewById(R.id.keyguard_bouncer_user_switcher);
+            }
+
+            mUserIconView = mView.findViewById(R.id.user_icon);
+            Drawable icon = UserIcons.getDefaultUserIcon(v.getContext().getResources(), 0, false);
+            mUserIconView.setImageDrawable(icon);
+
+            updateSecurityViewLocation();
+
+            mUserSwitcher = mView.findViewById(R.id.user_switcher_header);
+            setupUserSwitcher();
+        }
+
+        @Override
+        public void reset() {
+            if (mPopup != null) {
+                mPopup.dismiss();
+                mPopup = null;
+            }
+        }
+
+        @Override
+        public void startAppearAnimation(SecurityMode securityMode) {
+            // IME insets animations handle alpha and translation
+            if (securityMode == SecurityMode.Password) {
+                return;
+            }
+
+            mUserSwitcherViewGroup.setAlpha(0f);
+            ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
+                    1f);
+            alphaAnim.setInterpolator(Interpolators.ALPHA_IN);
+            alphaAnim.setDuration(500);
+            alphaAnim.start();
+        }
+
+        @Override
+        public void startDisappearAnimation(SecurityMode securityMode) {
+            // IME insets animations handle alpha and translation
+            if (securityMode == SecurityMode.Password) {
+                return;
+            }
+
+            int yTranslation = mView.getContext().getResources().getDimensionPixelSize(
+                    R.dimen.disappear_y_translation);
+
+            AnimatorSet anims = new AnimatorSet();
+            ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
+            ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f);
+
+            anims.setInterpolator(Interpolators.STANDARD_ACCELERATE);
+            anims.playTogether(alphaAnim, yAnim);
+            anims.start();
+        }
+
+        private void setupUserSwitcher() {
+            String currentUserName = mUserSwitcherController.getCurrentUserName();
+            mUserSwitcher.setText(currentUserName);
+
+            ViewGroup anchor = mView.findViewById(R.id.user_switcher_anchor);
+            BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
+                @Override
+                public View getView(int position, View convertView, ViewGroup parent) {
+                    UserRecord item = getItem(position);
+                    TextView view = (TextView) convertView;
+                    if (view == null) {
+                        view = (TextView) LayoutInflater.from(parent.getContext()).inflate(
+                                R.layout.keyguard_bouncer_user_switcher_item,
+                                parent,
+                                false);
+                    }
+                    view.setText(getName(parent.getContext(), item));
+                    return view;
+                }
+            };
+
+            if (adapter.getCount() < 2) {
+                // The drop down arrow is at index 1
+                ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(0);
+                anchor.setClickable(false);
+                return;
+            } else {
+                ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255);
+            }
+
+            anchor.setClickable(true);
+            anchor.setOnTouchListener((v, ev) -> {
+                if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                    mFalsingCollector.avoidGesture();
+                    mPopup = new KeyguardUserSwitcherPopupMenu(v.getContext(),
+                            mFalsingCollector);
+                    mPopup.setAnchorView(anchor);
+                    mPopup.setAdapter(adapter);
+                    mPopup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                            public void onItemClick(AdapterView parent, View view, int pos,
+                                    long id) {
+                                mFalsingCollector.avoidGesture();
+
+                                // - 1 to account for the header view
+                                UserRecord user = adapter.getItem(pos - 1);
+                                if (!user.isCurrent) {
+                                    adapter.onUserListItemClicked(user);
+                                }
+                                mPopup.dismiss();
+                                mPopup = null;
+                            }
+                        });
+                    mPopup.show();
+                }
+                return true;
+            });
+        }
+
+        /**
+         * Each view will get half the width. Yes, it would be easier to use something other than
+         * FrameLayout but it was too disruptive to downstream projects to change.
+         */
+        @Override
+        public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
+            return MeasureSpec.makeMeasureSpec(
+                    MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
+                    MeasureSpec.getMode(parentWidthMeasureSpec));
+        }
+
+        @Override
+        public void updateSecurityViewLocation() {
+            if (mView.getContext().getResources().getConfiguration().orientation
+                    == Configuration.ORIENTATION_PORTRAIT) {
+                updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
+                updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
+            } else {
+                updateViewGravity(mViewFlipper, Gravity.RIGHT | Gravity.BOTTOM);
+                updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.TOP);
+            }
+        }
+
+        private void updateViewGravity(View v, int gravity) {
+            FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+            lp.gravity = gravity;
+            v.setLayoutParams(lp);
         }
     }
 
@@ -705,7 +926,7 @@
      * Logic to enabled one-handed bouncer mode. Supports animating the bouncer
      * between alternate sides of the display.
      */
-    private static class OneHandedModeLogic implements ModeLogic {
+    static class OneHandedViewMode implements ViewMode {
         @Nullable private ValueAnimator mRunningOneHandedAnimator;
         private ViewGroup mView;
         private KeyguardSecurityViewFlipper mViewFlipper;
@@ -713,7 +934,9 @@
 
         @Override
         public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
-                @NonNull KeyguardSecurityViewFlipper viewFlipper) {
+                @NonNull KeyguardSecurityViewFlipper viewFlipper,
+                @NonNull FalsingCollector falsingCollector,
+                @NonNull UserSwitcherController userSwitcherController) {
             mView = v;
             mViewFlipper = viewFlipper;
             mGlobalSettings = globalSettings;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 4035229..6b73a32 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -54,6 +54,7 @@
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.settings.GlobalSettings;
 
@@ -78,6 +79,7 @@
     private final SecurityCallback mSecurityCallback;
     private final ConfigurationController mConfigurationController;
     private final FalsingCollector mFalsingCollector;
+    private final UserSwitcherController mUserSwitcherController;
     private final GlobalSettings mGlobalSettings;
 
     private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
@@ -232,6 +234,7 @@
             KeyguardSecurityViewFlipperController securityViewFlipperController,
             ConfigurationController configurationController,
             FalsingCollector falsingCollector,
+            UserSwitcherController userSwitcherController,
             GlobalSettings globalSettings) {
         super(view);
         mLockPatternUtils = lockPatternUtils;
@@ -247,6 +250,7 @@
         mConfigurationController = configurationController;
         mLastOrientation = getResources().getConfiguration().orientation;
         mFalsingCollector = falsingCollector;
+        mUserSwitcherController = userSwitcherController;
         mGlobalSettings = globalSettings;
     }
 
@@ -343,14 +347,14 @@
 
     public void startAppearAnimation() {
         if (mCurrentSecurityMode != SecurityMode.None) {
+            mView.startAppearAnimation(mCurrentSecurityMode);
             getCurrentSecurityController().startAppearAnimation();
         }
     }
 
     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
-        mView.startDisappearAnimation(getCurrentSecurityMode());
-
         if (mCurrentSecurityMode != SecurityMode.None) {
+            mView.startDisappearAnimation(mCurrentSecurityMode);
             return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable);
         }
 
@@ -506,15 +510,16 @@
     }
 
     private void configureMode() {
-        // One-handed mode and user-switcher are currently mutually exclusive, and enforced here
+        boolean useSimSecurity = mCurrentSecurityMode == SecurityMode.SimPin
+                || mCurrentSecurityMode == SecurityMode.SimPuk;
         int mode = KeyguardSecurityContainer.MODE_DEFAULT;
-        if (canDisplayUserSwitcher()) {
+        if (canDisplayUserSwitcher() && !useSimSecurity) {
             mode = KeyguardSecurityContainer.MODE_USER_SWITCHER;
         } else if (canUseOneHandedBouncer()) {
             mode = KeyguardSecurityContainer.MODE_ONE_HANDED;
         }
 
-        mView.initMode(mode, mGlobalSettings);
+        mView.initMode(mode, mGlobalSettings, mFalsingCollector, mUserSwitcherController);
     }
 
     public void reportFailedUnlockAttempt(int userId, int timeoutMs) {
@@ -605,6 +610,7 @@
         private final ConfigurationController mConfigurationController;
         private final FalsingCollector mFalsingCollector;
         private final GlobalSettings mGlobalSettings;
+        private final UserSwitcherController mUserSwitcherController;
 
         @Inject
         Factory(KeyguardSecurityContainer view,
@@ -619,6 +625,7 @@
                 KeyguardSecurityViewFlipperController securityViewFlipperController,
                 ConfigurationController configurationController,
                 FalsingCollector falsingCollector,
+                UserSwitcherController userSwitcherController,
                 GlobalSettings globalSettings) {
             mView = view;
             mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory;
@@ -632,6 +639,7 @@
             mConfigurationController = configurationController;
             mFalsingCollector = falsingCollector;
             mGlobalSettings = globalSettings;
+            mUserSwitcherController = userSwitcherController;
         }
 
         public KeyguardSecurityContainerController create(
@@ -640,7 +648,8 @@
                     mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                     mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                     mKeyguardStateController, securityCallback, mSecurityViewFlipperController,
-                    mConfigurationController, mFalsingCollector, mGlobalSettings);
+                    mConfigurationController, mFalsingCollector, mUserSwitcherController,
+                    mGlobalSettings);
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index e01e17d..4d2391a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -83,16 +83,6 @@
         return "";
     }
 
-    /**
-      * Translate the entire view, and optionally inform the wrapped view of the progress
-      * so it can animate with the parent.
-      */
-    public void animateForIme(int translationY, float interpolatedFraction, boolean appearingAnim) {
-        super.setTranslationY(translationY);
-        KeyguardInputView v = getSecurityView();
-        if (v != null) v.animateForIme(interpolatedFraction, appearingAnim);
-    }
-
     @Override
     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
         return p instanceof LayoutParams;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 0d72c93..03b647b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -92,11 +92,6 @@
     }
 
     @Override
-    public boolean startDisappearAnimation(Runnable finishRunnable) {
-        return false;
-    }
-
-    @Override
     public CharSequence getTitle() {
         return getContext().getString(
                 com.android.internal.R.string.keyguard_accessibility_sim_puk_unlock);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
new file mode 100644
index 0000000..ca31b40d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -0,0 +1,90 @@
+/*
+ * 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.keyguard;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ListPopupWindow;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
+
+/**
+ * Custom user-switcher for use on the bouncer.
+ */
+public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow {
+    private Context mContext;
+    private FalsingCollector mFalsingCollector;
+    private int mLastHeight = -1;
+    private View.OnLayoutChangeListener mLayoutListener = (v, l, t, r, b, ol, ot, or, ob) -> {
+        int height = -v.getMeasuredHeight() + getAnchorView().getHeight();
+        if (height != mLastHeight) {
+            mLastHeight = height;
+            setVerticalOffset(height);
+            KeyguardUserSwitcherPopupMenu.super.show();
+        }
+    };
+
+    public KeyguardUserSwitcherPopupMenu(@NonNull Context context,
+            @NonNull FalsingCollector falsingCollector) {
+        super(context);
+        mContext = context;
+        mFalsingCollector = falsingCollector;
+        Resources res = mContext.getResources();
+        setBackgroundDrawable(
+                res.getDrawable(R.drawable.keyguard_user_switcher_popup_bg, context.getTheme()));
+        setModal(true);
+        setOverlapAnchor(true);
+    }
+
+    /**
+      * Show the dialog.
+      */
+    @Override
+    public void show() {
+        // need to call show() first in order to construct the listView
+        super.show();
+        ListView listView = getListView();
+
+        // This will force the popupwindow to show upward instead of drop down
+        listView.addOnLayoutChangeListener(mLayoutListener);
+
+        TextView header = (TextView) LayoutInflater.from(mContext).inflate(
+                R.layout.keyguard_bouncer_user_switcher_item, listView, false);
+        header.setText(mContext.getResources().getString(
+                R.string.accessibility_multi_user_switch_switcher));
+        listView.addHeaderView(header);
+
+        listView.setOnTouchListener((v, ev) -> {
+            if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+                mFalsingCollector.avoidGesture();
+            }
+            return false;
+        });
+    }
+
+    @Override
+    public void dismiss() {
+        getListView().removeOnLayoutChangeListener(mLayoutListener);
+        super.dismiss();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index f725b9f..da0069f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -378,26 +378,26 @@
 
         @Override
         public void onAccessPointsChanged(final List<WifiEntry> accessPoints) {
-            mAccessPoints = accessPoints.toArray(new WifiEntry[accessPoints.size()]);
-            filterUnreachableAPs();
+            mAccessPoints = filterUnreachableAPs(accessPoints);
 
             updateItems();
         }
 
         /** Filter unreachable APs from mAccessPoints */
-        private void filterUnreachableAPs() {
+        private WifiEntry[] filterUnreachableAPs(List<WifiEntry> unfiltered) {
             int numReachable = 0;
-            for (WifiEntry ap : mAccessPoints) {
+            for (WifiEntry ap : unfiltered) {
                 if (isWifiEntryReachable(ap)) numReachable++;
             }
-            if (numReachable != mAccessPoints.length) {
-                WifiEntry[] unfiltered = mAccessPoints;
-                mAccessPoints = new WifiEntry[numReachable];
+            if (numReachable != unfiltered.size()) {
+                WifiEntry[] accessPoints = new WifiEntry[numReachable];
                 int i = 0;
                 for (WifiEntry ap : unfiltered) {
-                    if (isWifiEntryReachable(ap)) mAccessPoints[i++] = ap;
+                    if (isWifiEntryReachable(ap)) accessPoints[i++] = ap;
                 }
+                return accessPoints;
             }
+            return unfiltered.toArray(new WifiEntry[0]);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index aa3b3e1..ad1c232 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -74,7 +74,7 @@
     private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
     private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
     private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
-    private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
+    private static final int FP_ATTEMPTS_BEFORE_SHOW_BOUNCER = 2;
 
     @IntDef(prefix = { "MODE_" }, value = {
             MODE_NONE,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index a64e579..4e68b19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3419,12 +3419,6 @@
                     mStatusBarStateController.setState(KEYGUARD);
                 }
                 return true;
-            case StatusBarState.SHADE:
-
-                // This gets called in the middle of the touch handling, where the state is still
-                // that we are tracking the panel. Collapse the panel after this is done.
-                mView.post(mPostCollapseRunnable);
-                return false;
             default:
                 return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 9af79a9..53bfd77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -933,7 +933,6 @@
 
     private void abortAnimations() {
         cancelHeightAnimator();
-        mView.removeCallbacks(mPostCollapseRunnable);
         mView.removeCallbacks(mFlingCollapseRunnable);
     }
 
@@ -1110,13 +1109,6 @@
         return onMiddleClicked();
     }
 
-    protected final Runnable mPostCollapseRunnable = new Runnable() {
-        @Override
-        public void run() {
-            collapse(false /* delayed */, 1.0f /* speedUpFactor */);
-        }
-    };
-
     protected abstract boolean onMiddleClicked();
 
     protected abstract boolean isDozing();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index ec7e93b..b9386bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -15,12 +15,14 @@
  */
 package com.android.systemui.statusbar.phone
 
+import android.content.res.Configuration
 import android.graphics.Point
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver
 import com.android.systemui.R
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.UNFOLD_STATUS_BAR
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -35,9 +37,16 @@
     view: PhoneStatusBarView,
     @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
     private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
-    touchEventHandler: PhoneStatusBarView.TouchEventHandler
+    touchEventHandler: PhoneStatusBarView.TouchEventHandler,
+    private val configurationController: ConfigurationController
 ) : ViewController<PhoneStatusBarView>(view) {
 
+    private val configurationListener = object : ConfigurationController.ConfigurationListener {
+        override fun onConfigChanged(newConfig: Configuration?) {
+            mView.updateResources()
+        }
+    }
+
     override fun onViewAttached() {
         moveFromCenterAnimationController?.let { animationController ->
             val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
@@ -66,11 +75,13 @@
         }
 
         progressProvider?.setReadyToHandleTransition(true)
+        configurationController.addCallback(configurationListener)
     }
 
     override fun onViewDetached() {
         progressProvider?.setReadyToHandleTransition(false)
         moveFromCenterAnimationController?.onViewDetached()
+        configurationController.removeCallback(configurationListener)
     }
 
     init {
@@ -116,7 +127,8 @@
     class Factory @Inject constructor(
         private val unfoldComponent: Optional<SysUIUnfoldComponent>,
         @Named(UNFOLD_STATUS_BAR)
-        private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>
+        private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+        private val configurationController: ConfigurationController
     ) {
         fun create(
             view: PhoneStatusBarView,
@@ -128,7 +140,8 @@
                 unfoldComponent.map {
                     it.getStatusBarMoveFromCenterAnimationController()
                 }.getOrNull(),
-                touchEventHandler
+                touchEventHandler,
+                configurationController
             )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index a54251a..b4fed2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -218,10 +218,6 @@
         return getStatusBar().getNotificationShadeWindowView();
     }
 
-    protected PhoneStatusBarView getStatusBarView() {
-        return (PhoneStatusBarView) getStatusBar().getStatusBarView();
-    }
-
     private NotificationPanelViewController getNotificationPanelViewController() {
         return getStatusBar().getPanelController();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index 1ad9fa6..f6e19bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -18,6 +18,7 @@
 
 import android.view.View
 import androidx.constraintlayout.motion.widget.MotionLayout
+import com.android.settingslib.Utils
 import com.android.systemui.R
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.battery.BatteryMeterView
@@ -49,7 +50,7 @@
 
     private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
     // TODO(b/194178072) Handle RSSI hiding when multi carrier
-    private val iconManager: StatusBarIconController.IconManager
+    private val iconManager: StatusBarIconController.TintedIconManager
     private val qsCarrierGroupController: QSCarrierGroupController
     private var visible = false
         set(value) {
@@ -117,7 +118,9 @@
         batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
 
         val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
-        iconManager = StatusBarIconController.IconManager(iconContainer, featureFlags)
+        iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
+        iconManager.setTint(Utils.getColorAttrDefaultColor(statusBar.context,
+                android.R.attr.textColorPrimary))
         qsCarrierGroupController = qsCarrierGroupControllerBuilder
                 .setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
                 .build()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 0f0a2f0..6c0b717 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2715,9 +2715,6 @@
             mStatusBarWindowController.refreshStatusBarHeight();
         }
 
-        if (mStatusBarView != null) {
-            mStatusBarView.updateResources();
-        }
         if (mNotificationPanelViewController != null) {
             mNotificationPanelViewController.updateResources();
         }
@@ -4192,7 +4189,7 @@
 
             if (userSetup != mUserSetup) {
                 mUserSetup = userSetup;
-                if (!mUserSetup && mStatusBarView != null) {
+                if (!mUserSetup) {
                     animateCollapseQuickSettings();
                 }
                 if (mNotificationPanelViewController != null) {
@@ -4307,7 +4304,7 @@
                     updateTheme();
                     mNavigationBarController.touchAutoDim(mDisplayId);
                     Trace.beginSection("StatusBar#updateKeyguardState");
-                    if (mState == StatusBarState.KEYGUARD && mStatusBarView != null) {
+                    if (mState == StatusBarState.KEYGUARD) {
                         mNotificationPanelViewController.cancelPendingPanelCollapse();
                     }
                     updateDozingState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
index abb7449..b391de3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
@@ -529,9 +529,7 @@
             if (StatusBar.DEBUG_WINDOW_STATE) {
                 Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state));
             }
-            if (mStatusBar.getStatusBarView() != null
-                    && !showing
-                    && mStatusBarStateController.getState() == StatusBarState.SHADE) {
+            if (!showing && mStatusBarStateController.getState() == StatusBarState.SHADE) {
                 mNotificationPanelViewController.collapsePanel(
                             false /* animate */, false /* delayed */, 1.0f /* speedUpFactor */);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index dc8dc99..79ee746 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -836,6 +836,11 @@
         mRootView = notificationShadeWindowView;
     }
 
+    @VisibleForTesting
+    public KeyguardStateController getKeyguardStateController() {
+        return mKeyguardStateController;
+    }
+
     public static abstract class BaseUserAdapter extends BaseAdapter {
 
         final UserSwitcherController mController;
@@ -843,7 +848,7 @@
 
         protected BaseUserAdapter(UserSwitcherController controller) {
             mController = controller;
-            mKeyguardStateController = controller.mKeyguardStateController;
+            mKeyguardStateController = controller.getKeyguardStateController();
             controller.addAdapter(new WeakReference<>(this));
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 030464a..98ce138 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -50,6 +50,7 @@
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import org.junit.Before;
@@ -111,6 +112,8 @@
     private FalsingCollector mFalsingCollector;
     @Mock
     private GlobalSettings mGlobalSettings;
+    @Mock
+    private UserSwitcherController mUserSwitcherController;
     private Configuration mConfiguration;
 
     private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@@ -144,8 +147,8 @@
                 mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
                 mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
                 mKeyguardStateController, mKeyguardSecurityViewFlipperController,
-                mConfigurationController, mFalsingCollector, mGlobalSettings)
-                .create(mSecurityCallback);
+                mConfigurationController, mFalsingCollector, mUserSwitcherController,
+                mGlobalSettings).create(mSecurityCallback);
     }
 
     @Test
@@ -182,13 +185,15 @@
     public void onResourcesUpdate_callsThroughOnRotationChange() {
         // Rotation is the same, shouldn't cause an update
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings);
+        verify(mView, never()).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
 
         // Update rotation. Should trigger update
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
 
         mKeyguardSecurityContainerController.updateResources();
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
     }
 
     private void touchDownLeftSide() {
@@ -245,7 +250,8 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
     }
 
     @Test
@@ -256,7 +262,8 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern);
-        verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings);
+        verify(mView).initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
     }
 
     @Test
@@ -267,6 +274,7 @@
                 .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController);
 
         mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password);
-        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings);
+        verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index c751081..ea7940a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -16,21 +16,27 @@
 
 package com.android.keyguard;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowInsets.Type.ime;
 import static android.view.WindowInsets.Type.systemBars;
 
 import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
 import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.pm.UserInfo;
+import android.content.res.Configuration;
 import android.graphics.Insets;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
+import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -39,17 +45,26 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherController.UserRecord;
 import com.android.systemui.util.settings.GlobalSettings;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.ArrayList;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper()
@@ -67,28 +82,43 @@
     private KeyguardSecurityViewFlipper mSecurityViewFlipper;
     @Mock
     private GlobalSettings mGlobalSettings;
+    @Mock
+    private FalsingCollector mFalsingCollector;
+    @Mock
+    private UserSwitcherController mUserSwitcherController;
+    @Mock
+    private KeyguardStateController mKeyguardStateController;
+    @Captor
+    private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutCaptor;
 
     private KeyguardSecurityContainer mKeyguardSecurityContainer;
+    private FrameLayout.LayoutParams mSecurityViewFlipperLayoutParams;
 
     @Before
     public void setup() {
         // Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
         // the real references (rather than the TestableResources that this call creates).
         mContext.ensureTestableResources();
-        FrameLayout.LayoutParams securityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+        mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
+                MATCH_PARENT, MATCH_PARENT);
 
         when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
-        when(mSecurityViewFlipper.getLayoutParams()).thenReturn(securityViewFlipperLayoutParams);
+        when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
         mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
         mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
         mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+
+        when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
+        when(mUserSwitcherController.getKeyguardStateController())
+                .thenReturn(mKeyguardStateController);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
     }
 
     @Test
     public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
-        mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings);
+        mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
 
         int halfWidthMeasureSpec =
                 View.MeasureSpec.makeMeasureSpec(SCREEN_WIDTH / 2, View.MeasureSpec.EXACTLY);
@@ -99,7 +129,8 @@
 
     @Test
     public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
-        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
 
         mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
         verify(mSecurityViewFlipper).measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
@@ -110,7 +141,8 @@
         int imeInsetAmount = 100;
         int systemBarInsetAmount = 10;
 
-        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
         Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -134,7 +166,8 @@
         int imeInsetAmount = 0;
         int systemBarInsetAmount = 10;
 
-        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings);
+        mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
 
         Insets imeInset = Insets.of(0, 0, 0, imeInsetAmount);
         Insets systemBarInset = Insets.of(0, 0, 0, systemBarInsetAmount);
@@ -154,7 +187,8 @@
 
     private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
         int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
-        mKeyguardSecurityContainer.initMode(mode, mGlobalSettings);
+        mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingCollector,
+                mUserSwitcherController);
 
         mKeyguardSecurityContainer.measure(FAKE_MEASURE_SPEC, FAKE_MEASURE_SPEC);
         mKeyguardSecurityContainer.layout(0, 0, SCREEN_WIDTH, SCREEN_WIDTH);
@@ -189,4 +223,92 @@
         mKeyguardSecurityContainer.updatePositionByTouchX(1f);
         verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
     }
+
+    @Test
+    public void testUserSwitcherModeViewGravityLandscape() {
+        // GIVEN one user has been setup and in landscape
+        when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+        Configuration config = new Configuration();
+        config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        when(getContext().getResources().getConfiguration()).thenReturn(config);
+
+        // WHEN UserSwitcherViewMode is initialized and config has changed
+        setupUserSwitcher();
+        reset(mSecurityViewFlipper);
+        when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+        mKeyguardSecurityContainer.onConfigurationChanged(config);
+
+        // THEN views are oriented side by side
+        verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
+        assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.RIGHT | Gravity.BOTTOM);
+        ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+                R.id.keyguard_bouncer_user_switcher);
+        assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+                .isEqualTo(Gravity.LEFT | Gravity.TOP);
+    }
+
+    @Test
+    public void testUserSwitcherModeViewGravityPortrait() {
+        // GIVEN one user has been setup and in landscape
+        when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+        Configuration config = new Configuration();
+        config.orientation = Configuration.ORIENTATION_PORTRAIT;
+        when(getContext().getResources().getConfiguration()).thenReturn(config);
+
+        // WHEN UserSwitcherViewMode is initialized and config has changed
+        setupUserSwitcher();
+        reset(mSecurityViewFlipper);
+        when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+        mKeyguardSecurityContainer.onConfigurationChanged(config);
+
+        // THEN views are both centered horizontally
+        verify(mSecurityViewFlipper).setLayoutParams(mLayoutCaptor.capture());
+        assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(Gravity.CENTER_HORIZONTAL);
+        ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
+                R.id.keyguard_bouncer_user_switcher);
+        assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
+                .isEqualTo(Gravity.CENTER_HORIZONTAL);
+    }
+
+    @Test
+    public void testLessThanTwoUsersDoesNotAllowDropDown() {
+        // GIVEN one user has been setup
+        when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
+
+        // WHEN UserSwitcherViewMode is initialized
+        setupUserSwitcher();
+
+        // THEN the UserSwitcher anchor should not be clickable
+        ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
+        assertThat(anchor.isClickable()).isFalse();
+    }
+
+    @Test
+    public void testTwoOrMoreUsersDoesAllowDropDown() {
+        // GIVEN one user has been setup
+        when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(2));
+
+        // WHEN UserSwitcherViewMode is initialized
+        setupUserSwitcher();
+
+        // THEN the UserSwitcher anchor should not be clickable
+        ViewGroup anchor = mKeyguardSecurityContainer.findViewById(R.id.user_switcher_anchor);
+        assertThat(anchor.isClickable()).isTrue();
+    }
+
+    private void setupUserSwitcher() {
+        mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
+                mGlobalSettings, mFalsingCollector, mUserSwitcherController);
+    }
+
+    private ArrayList<UserRecord> buildUserRecords(int count) {
+        ArrayList<UserRecord> users = new ArrayList<>();
+        for (int i = 0; i < count; ++i) {
+            UserInfo info = new UserInfo(i /* id */, "Name: " + i, null /* iconPath */,
+                    0 /* flags */);
+            users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
+                    false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */));
+        }
+        return users;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 07debe6..c3349f1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -381,16 +381,15 @@
     }
 
     @Test
-    public void onUdfpsConsecutivelyFailedThreeTimes_showBouncer() {
+    public void onUdfpsConsecutivelyFailedTwoTimes_showBouncer() {
         // GIVEN UDFPS is supported
         when(mUpdateMonitor.isUdfpsSupported()).thenReturn(true);
 
-        // WHEN udfps fails twice - then don't show the bouncer
-        mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
+        // WHEN udfps fails once - then don't show the bouncer
         mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
         verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
 
-        // WHEN udfps fails the third time
+        // WHEN udfps fails the second time
         mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
 
         // THEN show the bouncer
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 7d266e9..235de1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -26,6 +26,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.unfold.SysUIUnfoldComponent
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -57,6 +58,8 @@
     private lateinit var sysuiUnfoldComponent: SysUIUnfoldComponent
     @Mock
     private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
+    @Mock
+    private lateinit var configurationController: ConfigurationController
 
     private lateinit var view: PhoneStatusBarView
     private lateinit var controller: PhoneStatusBarViewController
@@ -116,7 +119,8 @@
     private fun createController(view: PhoneStatusBarView): PhoneStatusBarViewController {
         return PhoneStatusBarViewController.Factory(
             Optional.of(sysuiUnfoldComponent),
-            Optional.of(progressProvider)
+            Optional.of(progressProvider),
+            configurationController
         ).create(view, touchEventHandler)
     }
 
diff --git a/services/companion/Android.bp b/services/companion/Android.bp
index e3926b4..d3ef6dc 100644
--- a/services/companion/Android.bp
+++ b/services/companion/Android.bp
@@ -24,5 +24,8 @@
         type: "stream",
     },
     srcs: [":services.companion-sources"],
-    libs: ["services.core"],
+    libs: [
+        "app-compat-annotations",
+        "services.core",
+    ],
 }
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
new file mode 100644
index 0000000..a6a8793
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.companion.virtual;
+
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.annotation.NonNull;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.os.Build;
+import android.os.UserHandle;
+import android.window.DisplayWindowPolicyController;
+
+import java.util.List;
+
+
+/**
+ * A controller to control the policies of the windows that can be displayed on the virtual display.
+ */
+class GenericWindowPolicyController extends DisplayWindowPolicyController {
+
+    /**
+     * If required, allow the secure activity to display on remote device since
+     * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
+
+    GenericWindowPolicyController(int windowFlags, int systemWindowFlags) {
+        setInterestedWindowFlags(windowFlags, systemWindowFlags);
+    }
+
+    @Override
+    public boolean canContainActivities(@NonNull List<ActivityInfo> activities) {
+        // Can't display all the activities if any of them don't want to be displayed.
+        final int activityCount = activities.size();
+        for (int i = 0; i < activityCount; i++) {
+            final ActivityInfo aInfo = activities.get(i);
+            if ((aInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
+            int systemWindowFlags) {
+        if ((activityInfo.flags & ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES) == 0) {
+            return false;
+        }
+        if (!CompatChanges.isChangeEnabled(ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE,
+                activityInfo.packageName,
+                UserHandle.getUserHandleForUid(activityInfo.applicationInfo.uid))) {
+            // TODO(b/201712607): Add checks for the apps that use SurfaceView#setSecure.
+            if ((windowFlags & FLAG_SECURE) != 0) {
+                return false;
+            }
+            if ((systemWindowFlags & SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onTopActivityChanged(ComponentName topActivity, int uid) {
+
+    }
+
+    @Override
+    public void onRunningAppsChanged(int[] runningUids) {
+
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 020d08f..2742608 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -16,6 +16,9 @@
 
 package com.android.server.companion.virtual;
 
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -131,11 +134,14 @@
 
         private final AssociationInfo mAssociationInfo;
         private final int mOwnerUid;
+        private final GenericWindowPolicyController mGenericWindowPolicyController;
         private final ArrayList<Integer> mDisplayIds = new ArrayList<>();
 
         private VirtualDeviceImpl(int ownerUid, IBinder token, AssociationInfo associationInfo) {
             mOwnerUid = ownerUid;
             mAssociationInfo = associationInfo;
+            mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE,
+                    SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
             try {
                 token.linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -167,8 +173,7 @@
                         "Virtual device already have a virtual display with ID " + displayId);
             }
             mDisplayIds.add(displayId);
-            // TODO(b/201712607): Return the corresponding DisplayWindowPolicyController.
-            return null;
+            return mGenericWindowPolicyController;
         }
 
         void onVirtualDisplayRemovedLocked(int displayId) {
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 8d9b13e..3bf6ca2 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -30,6 +30,7 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.communal.ICommunalManager;
+import android.app.communal.ICommunalModeListener;
 import android.app.compat.CompatChanges;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -42,6 +43,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.net.Uri;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
@@ -77,6 +80,8 @@
     private final PackageReceiver mPackageReceiver;
     private final PackageManager mPackageManager;
     private final DreamManagerInternal mDreamManagerInternal;
+    private final RemoteCallbackList<ICommunalModeListener> mListeners =
+            new RemoteCallbackList<>();
 
     private final ActivityInterceptorCallback mActivityInterceptorCallback =
             new ActivityInterceptorCallback() {
@@ -129,7 +134,7 @@
 
     @Override
     public void onStart() {
-        publishBinderService(Context.COMMUNAL_MANAGER_SERVICE, mBinderService);
+        publishBinderService(Context.COMMUNAL_SERVICE, mBinderService);
     }
 
     @Override
@@ -242,6 +247,27 @@
         return !isAppAllowed(appInfo);
     }
 
+    private void dispatchCommunalMode(boolean isShowing) {
+        synchronized (mListeners) {
+            int i = mListeners.beginBroadcast();
+            while (i > 0) {
+                i--;
+                try {
+                    mListeners.getBroadcastItem(i).onCommunalModeChanged(isShowing);
+                } catch (RemoteException e) {
+                    // Handled by the RemoteCallbackList.
+                }
+            }
+            mListeners.finishBroadcast();
+        }
+    }
+
+    private void enforceReadPermission() {
+        mContext.enforceCallingPermission(Manifest.permission.READ_COMMUNAL_STATE,
+                Manifest.permission.READ_COMMUNAL_STATE
+                        + "permission required to read communal state.");
+    }
+
     private final class BinderService extends ICommunalManager.Stub {
         /**
          * Sets whether or not we are in communal mode.
@@ -252,7 +278,43 @@
             mContext.enforceCallingPermission(Manifest.permission.WRITE_COMMUNAL_STATE,
                     Manifest.permission.WRITE_COMMUNAL_STATE
                             + "permission required to modify communal state.");
+            if (mCommunalViewIsShowing.get() == isShowing) {
+                return;
+            }
             mCommunalViewIsShowing.set(isShowing);
+            dispatchCommunalMode(isShowing);
+        }
+
+        /**
+         * Checks whether or not we are in communal mode.
+         */
+        @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+        @Override
+        public boolean isCommunalMode() {
+            enforceReadPermission();
+            return mCommunalViewIsShowing.get();
+        }
+
+        /**
+         * Adds a callback to execute when communal state changes.
+         */
+        @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+        public void addCommunalModeListener(ICommunalModeListener listener) {
+            enforceReadPermission();
+            synchronized (mListeners) {
+                mListeners.register(listener);
+            }
+        }
+
+        /**
+         * Removes an added callback that execute when communal state changes.
+         */
+        @RequiresPermission(Manifest.permission.READ_COMMUNAL_STATE)
+        public void removeCommunalModeListener(ICommunalModeListener listener) {
+            enforceReadPermission();
+            synchronized (mListeners) {
+                mListeners.unregister(listener);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index 6628802..e508260 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -243,21 +243,24 @@
 
         @Override
         public boolean requestFrontend(@NonNull TunerFrontendRequest request,
-                @NonNull int[] frontendHandle) throws RemoteException {
+                @NonNull int[] frontendHandle) {
             enforceTunerAccessPermission("requestFrontend");
             enforceTrmAccessPermission("requestFrontend");
             if (frontendHandle == null) {
-                throw new RemoteException("frontendHandle can't be null");
+                Slog.e(TAG, "frontendHandle can't be null");
+                return false;
             }
             synchronized (mLock) {
                 if (!checkClientExists(request.clientId)) {
-                    throw new RemoteException("Request frontend from unregistered client: "
+                    Slog.e(TAG, "Request frontend from unregistered client: "
                             + request.clientId);
+                    return false;
                 }
                 // If the request client is holding or sharing a frontend, throw an exception.
                 if (!getClientProfile(request.clientId).getInUseFrontendHandles().isEmpty()) {
-                    throw new RemoteException("Release frontend before requesting another one. "
-                            + "Client id: " + request.clientId);
+                    Slog.e(TAG, "Release frontend before requesting another one. Client id: "
+                            + request.clientId);
+                    return false;
                 }
                 return requestFrontendInternal(request, frontendHandle);
             }
@@ -1153,7 +1156,8 @@
             ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
             if (ownerClient != null) {
                 for (int shareOwnerId : ownerClient.getShareFeClientIds()) {
-                    clearFrontendAndClientMapping(getClientProfile(shareOwnerId));
+                    reclaimResource(shareOwnerId,
+                            TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND);
                 }
             }
         }
diff --git a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
index 17d7c51..d6db1b2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/communal/CommunalManagerServiceTest.java
@@ -123,6 +123,8 @@
 
         doNothing().when(mContextSpy).enforceCallingPermission(
                 eq(Manifest.permission.WRITE_COMMUNAL_STATE), anyString());
+        doNothing().when(mContextSpy).enforceCallingPermission(
+                eq(Manifest.permission.READ_COMMUNAL_STATE), anyString());
 
         mService = new CommunalManagerService(mContextSpy);
         mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
@@ -203,6 +205,18 @@
     }
 
     @Test
+    public void testIsCommunalMode_isTrue() throws RemoteException {
+        mBinder.setCommunalViewShowing(true);
+        assertThat(mBinder.isCommunalMode()).isTrue();
+    }
+
+    @Test
+    public void testIsCommunalMode_isFalse() throws RemoteException {
+        mBinder.setCommunalViewShowing(false);
+        assertThat(mBinder.isCommunalMode()).isFalse();
+    }
+
+    @Test
     public void testIntercept_unlocked_communalOff_appNotEnabled_showWhenLockedOff() {
         when(mKeyguardManager.isKeyguardLocked()).thenReturn(false);
         mAInfo.flags = 0;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6f92c31..7d24b76 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1063,6 +1063,12 @@
             "always_show_emergency_alert_onoff_bool";
 
     /**
+     * Default mobile network MTU value, in bytes.
+     * @hide
+     */
+    public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
+
+    /**
      * The data call retry configuration for different types of APN.
      * @hide
      */
@@ -2914,19 +2920,37 @@
             "signal_strength_nr_nsa_use_lte_as_primary_bool";
 
     /**
+     * String array of TCP buffer sizes per network type.
+     * The entries should be of the following form, with values in bytes:
+     * "network_name:read_min,read_default,read_max,write_min,write_default,write_max".
+     * For NR (5G), the following network names should be used:
+     * - NR_NSA: NR NSA, sub-6 frequencies
+     * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+     * - NR_SA: NR SA, sub-6 frequencies
+     * - NR_SA_MMWAVE: NR SA, mmwave frequencies
+     * @hide
+     */
+    public static final String KEY_TCP_BUFFERS_STRING_ARRAY = "tcp_buffers_string_array";
+
+    /**
      * String array of default bandwidth values per network type.
-     * The entries should be of form "network_name:downstream,upstream", with values in Kbps.
+     * The entries should be of form: "network_name:downlink,uplink", with values in Kbps.
+     * For NR (5G), the following network names should be used:
+     * - NR_NSA: NR NSA, sub-6 frequencies
+     * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+     * - NR_SA: NR SA, sub-6 frequencies
+     * - NR_SA_MMWAVE: NR SA, mmwave frequencies
      * @hide
      */
     public static final String KEY_BANDWIDTH_STRING_ARRAY = "bandwidth_string_array";
 
     /**
      * For NR (non-standalone), whether to use the LTE value instead of NR value as the default for
-     * upstream bandwidth. Downstream bandwidth will still use the NR value as the default.
+     * uplink bandwidth. Downlink bandwidth will still use the NR value as the default.
      * @hide
      */
-    public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL =
-            "bandwidth_nr_nsa_use_lte_value_for_upstream_bool";
+    public static final String KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL =
+            "bandwidth_nr_nsa_use_lte_value_for_uplink_bool";
 
     /**
      * Key identifying if voice call barring notification is required to be shown to the user.
@@ -3628,6 +3652,18 @@
     public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = "5g_watchdog_time_ms_long";
 
     /**
+     * Which NR types are unmetered. A string array containing the following keys:
+     * NR_NSA - NR NSA is unmetered for sub-6 frequencies
+     * NR_NSA_MMWAVE - NR NSA is unmetered for mmwave frequencies
+     * NR_SA - NR SA is unmetered for sub-6 frequencies
+     * NR_SA_MMWAVE - NR SA is unmetered for mmwave frequencies
+     * TODO: remove other unmetered keys and replace with this
+     * @hide
+     */
+    public static final String KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY =
+            "unmetered_network_types_string_array";
+
+    /**
      * Whether NR (non-standalone) should be unmetered for all frequencies.
      * If either {@link #KEY_UNMETERED_NR_NSA_MMWAVE_BOOL} or
      * {@link #KEY_UNMETERED_NR_NSA_SUB6_BOOL} are true, then this value will be ignored.
@@ -5483,6 +5519,7 @@
 
         sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
+        sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
         sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
                 "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
                         + "320000:5000,640000:5000,1280000:5000,1800000:5000",
@@ -5801,12 +5838,35 @@
                 CellSignalStrengthNr.USE_SSRSRP);
         sDefaults.putBoolean(KEY_SIGNAL_STRENGTH_NR_NSA_USE_LTE_AS_PRIMARY_BOOL, true);
         sDefaults.putStringArray(KEY_BANDWIDTH_STRING_ARRAY, new String[]{
-                "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA-IS95A:14,14", "CDMA-IS95B:14,14",
-                "1xRTT:30,30", "EvDo-rev.0:750,48", "EvDo-rev.A:950,550", "HSDPA:4300,620",
-                "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo-rev.B:1500,550", "eHRPD:750,48",
-                "HSPAP:13000,3400", "TD-SCDMA:115,115", "LTE:30000,15000", "NR_NSA:47000,18000",
-                "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000"});
-        sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPSTREAM_BOOL, false);
+                "GPRS:24,24", "EDGE:70,18", "UMTS:115,115", "CDMA:14,14",
+                "1xRTT:30,30", "EvDo_0:750,48", "EvDo_A:950,550", "HSDPA:4300,620",
+                "HSUPA:4300,1800", "HSPA:4300,1800", "EvDo_B:1500,550", "eHRPD:750,48",
+                "iDEN:14,14", "LTE:30000,15000", "HSPA+:13000,3400", "GSM:24,24",
+                "TD_SCDMA:115,115", "LTE_CA:30000,15000", "NR_NSA:47000,18000",
+                "NR_NSA_MMWAVE:145000,60000", "NR_SA:145000,60000", "NR_SA_MMWAVE:145000,60000"});
+        sDefaults.putStringArray(KEY_TCP_BUFFERS_STRING_ARRAY, new String[]{
+                "GPRS:4092,8760,48000,4096,8760,48000", "EDGE:4093,26280,70800,4096,16384,70800",
+                "UMTS:58254,349525,1048576,58254,349525,1048576",
+                "CDMA:4094,87380,262144,4096,16384,262144",
+                "1xRTT:16384,32768,131072,4096,16384,102400",
+                "EvDo_0:4094,87380,262144,4096,16384,262144",
+                "EvDo_A:4094,87380,262144,4096,16384,262144",
+                "HSDPA:61167,367002,1101005,8738,52429,262114",
+                "HSUPA:40778,244668,734003,16777,100663,301990",
+                "HSPA:40778,244668,734003,16777,100663,301990",
+                "EvDo_B:4094,87380,262144,4096,16384,262144",
+                "eHRPD:131072,262144,1048576,4096,16384,524288",
+                "iDEN:4094,87380,262144,4096,16384,262144",
+                "LTE:524288,1048576,2097152,262144,524288,1048576",
+                "HSPA+:122334,734003,2202010,32040,192239,576717",
+                "GSM:4092,8760,48000,4096,8760,48000",
+                "TD_SCDMA:58254,349525,1048576,58254,349525,1048576",
+                "LTE_CA:4096,6291456,12582912,4096,1048576,2097152",
+                "NR_NSA:2097152,6291456,16777216,512000,2097152,8388608",
+                "NR_NSA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608",
+                "NR_SA:2097152,6291456,16777216,512000,2097152,8388608",
+                "NR_SA_MMWAVE:2097152,6291456,16777216,512000,2097152,8388608"});
+        sDefaults.putBoolean(KEY_BANDWIDTH_NR_NSA_USE_LTE_VALUE_FOR_UPLINK_BOOL, false);
         sDefaults.putString(KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING, "rssi");
         sDefaults.putBoolean(KEY_CONFIG_SHOW_ORIG_DIAL_STRING_FOR_CDMA_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_CALL_BLOCKING_DISABLED_NOTIFICATION_ALWAYS_BOOL, false);
@@ -5832,6 +5892,7 @@
         sDefaults.putInt(KEY_NR_ADVANCED_CAPABLE_PCO_ID_INT, 0);
         sDefaults.putBoolean(KEY_ENABLE_NR_ADVANCED_WHILE_ROAMING_BOOL, true);
         sDefaults.putBoolean(KEY_LTE_ENDC_USING_USER_DATA_FOR_RRC_DETECTION_BOOL, false);
+        sDefaults.putStringArray(KEY_UNMETERED_NETWORK_TYPES_STRING_ARRAY, new String[0]);
         sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_BOOL, false);
         sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_MMWAVE_BOOL, false);
         sDefaults.putBoolean(KEY_UNMETERED_NR_NSA_SUB6_BOOL, false);
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 114f10d..1f336f7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2502,4 +2502,7 @@
      */
     CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
             String callingFeatureId);
+
+    /** Check if telephony new data stack is enabled. */
+    boolean isUsingNewDataStack();
 }