Merge "Use an iterator in removeChannelNotifications rather than index." into sc-dev
diff --git a/core/api/current.txt b/core/api/current.txt
index b40604e..974c5a5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -26547,9 +26547,6 @@
     field public static final int UNSUPPORTED = -1; // 0xffffffff
   }
 
-  public interface TunnelConnectionParams {
-  }
-
   public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
     method public abstract android.net.Uri.Builder buildUpon();
     method public int compareTo(android.net.Uri);
@@ -27118,7 +27115,7 @@
   }
 
   public static final class VcnGatewayConnectionConfig.Builder {
-    ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.TunnelConnectionParams);
+    ctor public VcnGatewayConnectionConfig.Builder(@NonNull String, @NonNull android.net.ipsec.ike.IkeTunnelConnectionParams);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int);
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build();
     method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int);
diff --git a/core/java/android/net/TunnelConnectionParams.java b/core/java/android/net/TunnelConnectionParams.java
deleted file mode 100644
index f5b3539..0000000
--- a/core/java/android/net/TunnelConnectionParams.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.net;
-
-/**
- * TunnelConnectionParams represents a configuration to set up a tunnel connection.
- *
- * <p>Concrete implementations for a control plane protocol should implement this interface.
- * Subclasses should be immutable data classes containing connection, authentication and
- * authorization parameters required to establish a tunnel connection.
- *
- * @see android.net.ipsec.ike.IkeTunnelConnectionParams
- */
-// TODO:b/186071626 Remove TunnelConnectionParams when non-updatable API stub can resolve
-// IkeTunnelConnectionParams
-public interface TunnelConnectionParams {}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 3c02cf0..eedeeb5 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -24,7 +24,7 @@
 import android.annotation.SuppressLint;
 import android.net.Network;
 import android.net.NetworkCapabilities;
-import android.net.TunnelConnectionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils;
 import android.os.PersistableBundle;
 import android.util.ArraySet;
@@ -159,7 +159,7 @@
     @NonNull private final String mGatewayConnectionName;
 
     private static final String TUNNEL_CONNECTION_PARAMS_KEY = "mTunnelConnectionParams";
-    @NonNull private TunnelConnectionParams mTunnelConnectionParams;
+    @NonNull private IkeTunnelConnectionParams mTunnelConnectionParams;
 
     private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
     @NonNull private final SortedSet<Integer> mExposedCapabilities;
@@ -176,7 +176,7 @@
     /** Builds a VcnGatewayConnectionConfig with the specified parameters. */
     private VcnGatewayConnectionConfig(
             @NonNull String gatewayConnectionName,
-            @NonNull TunnelConnectionParams tunnelConnectionParams,
+            @NonNull IkeTunnelConnectionParams tunnelConnectionParams,
             @NonNull Set<Integer> exposedCapabilities,
             @NonNull Set<Integer> underlyingCapabilities,
             @NonNull long[] retryIntervalsMs,
@@ -276,7 +276,7 @@
      * @hide
      */
     @NonNull
-    public TunnelConnectionParams getTunnelConnectionParams() {
+    public IkeTunnelConnectionParams getTunnelConnectionParams() {
         return mTunnelConnectionParams;
     }
 
@@ -419,7 +419,7 @@
      */
     public static final class Builder {
         @NonNull private final String mGatewayConnectionName;
-        @NonNull private final TunnelConnectionParams mTunnelConnectionParams;
+        @NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams;
         @NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
         @NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
         @NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -437,13 +437,13 @@
          *     VcnConfig} must be given a unique name. This name is used by the caller to
          *     distinguish between VcnGatewayConnectionConfigs configured on a single {@link
          *     VcnConfig}. This will be used as the identifier in VcnStatusCallback invocations.
-         * @param tunnelConnectionParams the tunnel connection configuration
-         * @see TunnelConnectionParams
+         * @param tunnelConnectionParams the IKE tunnel connection configuration
+         * @see IkeTunnelConnectionParams
          * @see VcnManager.VcnStatusCallback#onGatewayConnectionError
          */
         public Builder(
                 @NonNull String gatewayConnectionName,
-                @NonNull TunnelConnectionParams tunnelConnectionParams) {
+                @NonNull IkeTunnelConnectionParams tunnelConnectionParams) {
             Objects.requireNonNull(gatewayConnectionName, "gatewayConnectionName was null");
             Objects.requireNonNull(tunnelConnectionParams, "tunnelConnectionParams was null");
 
diff --git a/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java b/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
index 690e4e7..4bc5b49 100644
--- a/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
+++ b/core/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtils.java
@@ -16,7 +16,6 @@
 package android.net.vcn.persistablebundleutils;
 
 import android.annotation.NonNull;
-import android.net.TunnelConnectionParams;
 import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.ipsec.ike.TunnelModeChildSessionParams;
@@ -25,7 +24,7 @@
 import java.util.Objects;
 
 /**
- * Utility class to convert TunnelConnectionParams to/from PersistableBundle
+ * Utility class to convert Tunnel Connection Params to/from PersistableBundle
  *
  * @hide
  */
@@ -34,30 +33,28 @@
 
     private static final String PARAMS_TYPE_IKE = "IKE";
 
-    /** Serializes an TunnelConnectionParams to a PersistableBundle. */
+    /** Serializes an IkeTunnelConnectionParams to a PersistableBundle. */
     @NonNull
-    public static PersistableBundle toPersistableBundle(@NonNull TunnelConnectionParams params) {
+    public static PersistableBundle toPersistableBundle(@NonNull IkeTunnelConnectionParams params) {
         final PersistableBundle result = new PersistableBundle();
 
-        if (params instanceof IkeTunnelConnectionParams) {
-            result.putPersistableBundle(
-                    PARAMS_TYPE_IKE,
-                    IkeTunnelConnectionParamsUtils.serializeIkeParams(
-                            (IkeTunnelConnectionParams) params));
-            return result;
-        } else {
-            throw new UnsupportedOperationException("Invalid TunnelConnectionParams type");
-        }
+        result.putPersistableBundle(
+                PARAMS_TYPE_IKE,
+                IkeTunnelConnectionParamsUtils.serializeIkeParams(
+                        (IkeTunnelConnectionParams) params));
+        return result;
     }
 
-    /** Constructs an TunnelConnectionParams by deserializing a PersistableBundle. */
+    /** Constructs an IkeTunnelConnectionParams by deserializing a PersistableBundle. */
     @NonNull
-    public static TunnelConnectionParams fromPersistableBundle(@NonNull PersistableBundle in) {
+    public static IkeTunnelConnectionParams fromPersistableBundle(@NonNull PersistableBundle in) {
         Objects.requireNonNull(in, "PersistableBundle was null");
 
         if (in.keySet().size() != EXPECTED_BUNDLE_KEY_CNT) {
             throw new IllegalArgumentException(
-                    "Expect PersistableBundle to have one element but found: " + in.keySet());
+                    String.format(
+                            "Expect PersistableBundle to have %d element but found: %d",
+                            EXPECTED_BUNDLE_KEY_CNT, in.keySet()));
         }
 
         if (in.get(PARAMS_TYPE_IKE) != null) {
@@ -66,7 +63,7 @@
         }
 
         throw new IllegalArgumentException(
-                "Invalid TunnelConnectionParams type " + in.keySet().iterator().next());
+                "Invalid Tunnel Connection Params type " + in.keySet().iterator().next());
     }
 
     private static final class IkeTunnelConnectionParamsUtils {
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e47129e..0dbdb8f 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1226,6 +1226,7 @@
         return rect;
     }
 
+    @Nullable
     private static Class<?> getParameterType(int type) {
         switch (type) {
             case BaseReflectionAction.BOOLEAN:
@@ -1267,6 +1268,7 @@
         }
     }
 
+    @Nullable
     private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
             boolean async) {
         MethodArgs result;
@@ -1517,6 +1519,7 @@
             }
         }
 
+        @Nullable
         public Bitmap getBitmapForId(int id) {
             if (id == -1 || id >= mBitmaps.size()) {
                 return null;
@@ -1864,8 +1867,9 @@
             }
         }
 
+        @Nullable
         @Override
-        protected Object getParameterValue(View view) throws ActionException {
+        protected Object getParameterValue(@Nullable View view) throws ActionException {
             return this.value;
         }
 
@@ -1904,8 +1908,11 @@
             dest.writeInt(this.mResId);
         }
 
+        @Nullable
         @Override
-        protected Object getParameterValue(View view) throws ActionException {
+        protected Object getParameterValue(@Nullable View view) throws ActionException {
+            if (view == null) return null;
+
             Resources resources = view.getContext().getResources();
             try {
                 switch (this.mResourceType) {
@@ -2079,8 +2086,11 @@
             dest.writeInt(this.mUnit);
         }
 
+        @Nullable
         @Override
-        protected Object getParameterValue(View view) throws ActionException {
+        protected Object getParameterValue(@Nullable View view) throws ActionException {
+            if (view == null) return null;
+
             DisplayMetrics dm = view.getContext().getResources().getDisplayMetrics();
             try {
                 int data = TypedValue.createComplexDimension(this.mValue, this.mUnit);
@@ -3592,6 +3602,9 @@
         while (remoteViews.hasNext()) {
             RemoteViews view = remoteViews.next();
             SizeF size = view.getIdealSize();
+            if (size == null) {
+                throw new IllegalStateException("Expected RemoteViews to have ideal size");
+            }
             float newViewArea = size.getWidth() * size.getHeight();
             if (smallestView != null && !view.hasSameAppInfo(smallestView.mApplication)) {
                 throw new IllegalArgumentException(
@@ -5309,6 +5322,10 @@
         float bestSqDist = Float.MAX_VALUE;
         for (RemoteViews layout : mSizedRemoteViews) {
             SizeF layoutSize = layout.getIdealSize();
+            if (layoutSize == null) {
+                throw new IllegalStateException("Expected RemoteViews to have ideal size");
+            }
+
             if (fitsIn(layoutSize, widgetSize)) {
                 if (bestFit == null) {
                     bestFit = layout;
@@ -5342,7 +5359,7 @@
      */
     public RemoteViews getRemoteViewsToApply(@NonNull Context context,
             @Nullable SizeF widgetSize) {
-        if (!hasSizedRemoteViews()) {
+        if (!hasSizedRemoteViews() || widgetSize == null) {
             // If there isn't multiple remote views, fall back on the previous methods.
             return getRemoteViewsToApply(context);
         }
@@ -5419,7 +5436,7 @@
 
     /** @hide */
     public View apply(Context context, ViewGroup parent, InteractionHandler handler,
-            @NonNull SizeF size, @Nullable ColorResources colorResources) {
+            @Nullable SizeF size, @Nullable ColorResources colorResources) {
         RemoteViews rvToApply = getRemoteViewsToApply(context, size);
 
         View result = inflateView(context, rvToApply, parent, 0, colorResources);
@@ -5431,7 +5448,7 @@
         return inflateView(context, rv, parent, 0, null);
     }
 
-    private View inflateView(Context context, RemoteViews rv, ViewGroup parent,
+    private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
             @StyleRes int applyThemeResId, @Nullable ColorResources colorResources) {
         // RemoteViews may be built by an application installed in another
         // user. So build a context that loads resources from that user but
@@ -5447,8 +5464,7 @@
         if (applyThemeResId != 0) {
             inflationContext = new ContextThemeWrapper(inflationContext, applyThemeResId);
         }
-        LayoutInflater inflater = (LayoutInflater)
-                context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        LayoutInflater inflater = LayoutInflater.from(context);
 
         // Clone inflater so we load resources from correct context and
         // we don't add a filter to the static version returned by getSystemService.
@@ -5576,6 +5592,7 @@
             mResult = result;
         }
 
+        @Nullable
         @Override
         protected ViewTree doInBackground(Void... params) {
             try {
@@ -5860,6 +5877,7 @@
          * are in an array, the array's entries are 16 bytes each. We use this to work out the
          * location of all the positions of the various resources.
          */
+        @Nullable
         private static byte[] createCompiledResourcesContent(Context context,
                 SparseIntArray colorResources) throws IOException {
             byte[] content;
@@ -5897,6 +5915,7 @@
          *
          * @hide
          */
+        @Nullable
         public static ColorResources create(Context context, SparseIntArray colorMapping) {
             try {
                 byte[] contentBytes = createCompiledResourcesContent(context, colorMapping);
@@ -6020,7 +6039,8 @@
         }
     }
 
-    private static ApplicationInfo getApplicationInfo(String packageName, int userId) {
+    @Nullable
+    private static ApplicationInfo getApplicationInfo(@Nullable String packageName, int userId) {
         if (packageName == null) {
             return null;
         }
@@ -6096,6 +6116,7 @@
             }
         }
 
+        @Nullable
         public ViewTree findViewTreeById(@IdRes int id) {
             if (mRoot.getId() == id) {
                 return this;
@@ -6112,6 +6133,7 @@
             return null;
         }
 
+        @Nullable
         public ViewTree findViewTreeParentOf(ViewTree child) {
             if (mChildren == null) {
                 return null;
@@ -6134,6 +6156,7 @@
             createTree();
         }
 
+        @Nullable
         public <T extends View> T findViewById(@IdRes int id) {
             if (mChildren == null) {
                 return mRoot.findViewById(id);
@@ -6391,6 +6414,8 @@
          */
         @Nullable
         private static AdapterView<?> getAdapterViewAncestor(@Nullable View view) {
+            if (view == null) return null;
+
             View parent = (View) view.getParent();
             // Break the for loop on the first encounter of:
             //    1) an AdapterView,
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index ea0f704..4e96ae7 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -128,6 +128,8 @@
     private CharSequence mFallbackChatName;
     private CharSequence mFallbackGroupChatName;
     private CharSequence mConversationTitle;
+    private int mMessageSpacingStandard;
+    private int mMessageSpacingGroup;
     private int mNotificationHeaderExpandedPadding;
     private View mConversationHeader;
     private View mContentContainer;
@@ -241,6 +243,10 @@
         mContentContainer = findViewById(R.id.notification_action_list_margin_target);
         mExpandButtonAndContentContainer = findViewById(R.id.expand_button_and_content_container);
         mExpandButton = findViewById(R.id.expand_button);
+        mMessageSpacingStandard = getResources().getDimensionPixelSize(
+                R.dimen.notification_messaging_spacing);
+        mMessageSpacingGroup = getResources().getDimensionPixelSize(
+                R.dimen.notification_messaging_spacing_conversation_group);
         mNotificationHeaderExpandedPadding = getResources().getDimensionPixelSize(
                 R.dimen.conversation_header_expanded_padding_end);
         mContentMarginEnd = getResources().getDimensionPixelSize(
@@ -699,6 +705,10 @@
     }
 
     private void updatePaddingsBasedOnContentAvailability() {
+        // groups have avatars that need more spacing
+        mMessagingLinearLayout.setSpacing(
+                mIsOneToOne ? mMessageSpacingStandard : mMessageSpacingGroup);
+
         int messagingPadding = mIsOneToOne || mIsCollapsed
                 ? 0
                 // Add some extra padding to the messages, since otherwise it will overlap with the
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index 7cfd46c..cb1d387 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -17,6 +17,7 @@
 package com.android.internal.widget;
 
 import android.annotation.Nullable;
+import android.annotation.Px;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -251,6 +252,16 @@
         return super.drawChild(canvas, child, drawingTime);
     }
 
+    /**
+     * Set the spacing to be applied between views.
+     */
+    public void setSpacing(@Px int spacing) {
+        if (mSpacing != spacing) {
+            mSpacing = spacing;
+            requestLayout();
+        }
+    }
+
     @Override
     public LayoutParams generateLayoutParams(AttributeSet attrs) {
         return new LayoutParams(mContext, attrs);
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 43c0ec9..fc3ed63 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -367,6 +367,9 @@
     <!-- The spacing between messages in Notification.MessagingStyle -->
     <dimen name="notification_messaging_spacing">6dp</dimen>
 
+    <!-- The spacing between messages in Notification.MessagingStyle -->
+    <dimen name="notification_messaging_spacing_conversation_group">24dp</dimen>
+
     <!-- The rounding for messaging images -->
     <dimen name="messaging_image_rounding">4dp</dimen>
 
@@ -760,7 +763,7 @@
     <!-- The maximum size of the grayscale icon -->
     <dimen name="notification_grayscale_icon_max_size">256dp</dimen>
 
-    <dimen name="messaging_avatar_size">36dp</dimen>
+    <dimen name="messaging_avatar_size">48dp</dimen>
     <dimen name="conversation_avatar_size">48dp</dimen>
     <!-- start margin of the icon circle in the conversation's skin of the header -->
     <dimen name="conversation_icon_circle_start">28dp</dimen>
@@ -779,17 +782,17 @@
     <!-- size of the face pile icons -->
     <dimen name="conversation_face_pile_avatar_size">32dp</dimen>
     <!-- size of the face pile icons when the group is expanded -->
-    <dimen name="conversation_face_pile_avatar_size_group_expanded">25dp</dimen>
+    <dimen name="conversation_face_pile_avatar_size_group_expanded">@dimen/conversation_face_pile_avatar_size</dimen>
     <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded-->
-    <dimen name="conversation_badge_side_margin_group_expanded">22dp</dimen>
+    <dimen name="conversation_badge_side_margin_group_expanded">@dimen/conversation_badge_side_margin</dimen>
     <!-- Side margins of the conversation badge in relation to the conversation icon when the group is expanded-->
-    <dimen name="conversation_badge_side_margin_group_expanded_face_pile">18dp</dimen>
+    <dimen name="conversation_badge_side_margin_group_expanded_face_pile">@dimen/conversation_badge_side_margin</dimen>
     <!-- The width of the protection of the face pile layout-->
     <dimen name="conversation_face_pile_protection_width">2dp</dimen>
     <!-- The width of the protection of the face pile layout when expanded-->
-    <dimen name="conversation_face_pile_protection_width_expanded">1dp</dimen>
+    <dimen name="conversation_face_pile_protection_width_expanded">@dimen/conversation_face_pile_protection_width</dimen>
     <!-- The padding of the expanded message container-->
-    <dimen name="expanded_group_conversation_message_padding">17dp</dimen>
+    <dimen name="expanded_group_conversation_message_padding">32dp</dimen>
     <!-- The stroke width of the ring used to visually mark a conversation as important -->
     <dimen name="importance_ring_stroke_width">2dp</dimen>
     <!-- The maximum stroke width used for the animation shown when a conversation is marked as important -->
@@ -801,7 +804,7 @@
     <dimen name="conversation_icon_container_top_padding">20dp</dimen>
 
     <!-- The top padding of the conversation icon container when the avatar is small-->
-    <dimen name="conversation_icon_container_top_padding_small_avatar">9dp</dimen>
+    <dimen name="conversation_icon_container_top_padding_small_avatar">8dp</dimen>
 
     <!-- The padding of the conversation header when expanded. This is calculated from the expand button size + notification_content_margin_end -->
     <dimen name="conversation_header_expanded_padding_end">38dp</dimen>
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 439ae48..ad0d0e0 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -302,10 +302,6 @@
     <style name="TextAppearance.DeviceDefault.Notification.Time" parent="TextAppearance.Material.Notification.Time">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
-    <style name="TextAppearance.DeviceDefault.Notification.Conversation.AppName"
-           parent="TextAppearance.Material.Notification.Conversation.AppName">
-        <item name="fontFamily">@string/config_headlineFontFamilyMedium</item>
-    </style>
     <style name="TextAppearance.DeviceDefault.Widget" parent="TextAppearance.Material.Widget">
         <item name="fontFamily">@string/config_bodyFontFamily</item>
     </style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 3c4a5d4..eec6ae3 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -497,10 +497,6 @@
     <!-- unused; keep identical to parent -->
     <style name="TextAppearance.Material.Notification.Emphasis"/>
 
-    <style name="TextAppearance.Material.Notification.Conversation.AppName" parent="TextAppearance.Material.Notification.Title">
-        <item name="android:textSize">16sp</item>
-    </style>
-
     <style name="TextAppearance.Material.ListItem" parent="TextAppearance.Material.Subhead" />
     <style name="TextAppearance.Material.ListItemSecondary" parent="TextAppearance.Material.Body1" />
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9ac9e8e..d62f2bc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3083,6 +3083,7 @@
   <java-symbol type="string" name="negative_duration" />
 
   <java-symbol type="dimen" name="notification_messaging_spacing" />
+  <java-symbol type="dimen" name="notification_messaging_spacing_conversation_group" />
 
   <java-symbol type="dimen" name="notification_text_margin_top" />
   <java-symbol type="dimen" name="notification_inbox_item_top_padding" />
@@ -4090,7 +4091,6 @@
   <java-symbol type="dimen" name="button_inset_horizontal_material" />
   <java-symbol type="layout" name="conversation_face_pile_layout" />
   <java-symbol type="string" name="unread_convo_overflow" />
-  <java-symbol type="style" name="TextAppearance.DeviceDefault.Notification.Conversation.AppName" />
   <java-symbol type="drawable" name="conversation_badge_background" />
   <java-symbol type="drawable" name="conversation_badge_ring" />
   <java-symbol type="color" name="conversation_important_highlight" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index d5733e3..2650d9f 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -40,7 +40,7 @@
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
 
     <!-- Argentina: 5 digits, known short codes listed -->
-    <shortcode country="ar" pattern="\\d{5}" free="11711|28291" />
+    <shortcode country="ar" pattern="\\d{5}" free="11711|28291|44077" />
 
     <!-- Armenia: 3-4 digits, emergency numbers 10[123] -->
     <shortcode country="am" pattern="\\d{3,4}" premium="11[2456]1|3024" free="10[123]" />
@@ -76,14 +76,14 @@
     <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
 
     <!-- Chile: 4-5 digits (not confirmed), known premium codes listed -->
-    <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240" />
+    <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240|1038" />
 
     <!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
          http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
     <shortcode country="cn" premium="1066.*" free="1065.*" />
 
     <!-- Colombia: 1-6 digits (not confirmed) -->
-    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960" />
+    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739" />
 
     <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
@@ -156,7 +156,7 @@
     <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
 
     <!-- Kenya: 5 digits, known premium codes listed -->
-    <shortcode country="ke" pattern="\\d{5}" free="21725" />
+    <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520" />
 
     <!-- Kyrgyzstan: 4 digits, known premium codes listed -->
     <shortcode country="kg" pattern="\\d{4}" premium="415[2367]|444[69]" />
@@ -187,13 +187,13 @@
     <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="26259|46645|50025|50052|5050|76551|88778|9963" />
 
     <!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
-    <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
+    <shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288|66668" />
 
     <!-- The Netherlands, 4 digits, known premium codes listed, plus EU -->
     <shortcode country="nl" pattern="\\d{4}" premium="4466|5040" free="116\\d{3}|2223|6225|2223|1662" />
 
     <!-- Nigeria -->
-    <shortcode country="ng" pattern="\\d{1,5}" free="2441" />
+    <shortcode country="ng" pattern="\\d{1,5}" free="2441|55019" />
 
     <!-- Norway: 4-5 digits (not confirmed), known premium codes listed -->
     <shortcode country="no" pattern="\\d{4,5}" premium="2201|222[67]" free="2171" />
@@ -202,7 +202,7 @@
     <shortcode country="nz" pattern="\\d{3,4}" premium="3903|8995|4679" free="1737|176|2141|3067|3068|3110|4006|4053|4061|4062|4202|4300|4334|4412|4575|5626|8006|8681" />
 
     <!-- Peru: 4-5 digits (not confirmed), known premium codes listed -->
-    <shortcode country="pe" pattern="\\d{4,5}" free="9963" />
+    <shortcode country="pe" pattern="\\d{4,5}" free="9963|40777" />
 
     <!-- Philippines -->
     <shortcode country="ph" pattern="\\d{1,5}" free="2147|5495|5496" />
@@ -224,7 +224,7 @@
     <shortcode country="re" pattern="\\d{1,5}" free="38600,36300,36303,959" />
 
     <!-- Romania: 4 digits, plus EU: http://www.simplus.ro/en/resources/glossary-of-terms/ -->
-    <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}|3654|8360" />
+    <shortcode country="ro" pattern="\\d{4}" premium="12(?:63|66|88)|13(?:14|80)" free="116\\d{3}|3654|8360|3838" />
 
     <!-- Russia: 4 digits, known premium codes listed: http://smscoin.net/info/pricing-russia/ -->
     <shortcode country="ru" pattern="\\d{4}" premium="1(?:1[56]1|899)|2(?:09[57]|322|47[46]|880|990)|3[589]33|4161|44(?:4[3-9]|81)|77(?:33|81)|8424" free="6954|8501" standard="2037|2044"/>
@@ -252,7 +252,7 @@
     <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
 
     <!-- Turkey -->
-    <shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493" />
+    <shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493|3193" />
 
     <!-- Ukraine: 4 digits, known premium codes listed -->
     <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
@@ -268,6 +268,6 @@
     <shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
 
     <!-- South Africa -->
-    <shortcode country="za" pattern="\d{1,5}" free="44136" />
+    <shortcode country="za" pattern="\d{1,5}" free="44136|30791|36056" />
 
 </shortcodes>
diff --git a/graphics/java/android/graphics/drawable/ColorStateListDrawable.java b/graphics/java/android/graphics/drawable/ColorStateListDrawable.java
index 20cd825..423e66c 100644
--- a/graphics/java/android/graphics/drawable/ColorStateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorStateListDrawable.java
@@ -48,6 +48,12 @@
         setColorStateList(colorStateList);
     }
 
+    private ColorStateListDrawable(@NonNull ColorStateListDrawableState state) {
+        mState = state;
+        initializeColorDrawable();
+        onStateChange(getState());
+    }
+
     @Override
     public void draw(@NonNull Canvas canvas) {
         mColorDrawable.draw(canvas);
@@ -286,11 +292,6 @@
         }
     }
 
-    private ColorStateListDrawable(@NonNull ColorStateListDrawableState state) {
-        mState = state;
-        initializeColorDrawable();
-    }
-
     private void initializeColorDrawable() {
         mColorDrawable = new ColorDrawable();
         mColorDrawable.setCallback(this);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
index 6bc9a5c..58e9204 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt
@@ -25,7 +25,6 @@
 import com.android.server.wm.flicker.FlickerTestParameterFactory
 import com.android.server.wm.flicker.dsl.FlickerBuilder
 import com.android.server.wm.flicker.helpers.canSplitScreen
-import com.android.server.wm.flicker.helpers.openQuickstep
 import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
 import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
 import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -64,7 +63,6 @@
                 }
             }
             transitions {
-                device.openQuickstep(wmHelper)
                 if (device.canSplitScreen(wmHelper)) {
                     Assert.fail("Non-resizeable app should not enter split screen")
                 }
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 385e455..b0c8c61 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -442,6 +442,15 @@
         return ANDROID_IMAGE_DECODER_BAD_PARAMETER;
     }
 
+    const auto colorType = imageDecoder->getOutputInfo().colorType();
+    switch (colorType) {
+        case kN32_SkColorType:
+        case kRGBA_F16_SkColorType:
+            break;
+        default:
+            return ANDROID_IMAGE_DECODER_INVALID_STATE;
+    }
+
     if (imageDecoder->advanceFrame()) {
         return ANDROID_IMAGE_DECODER_SUCCESS;
     }
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index c5ba3d2..7f645ba 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -20,12 +20,14 @@
 <!-- This is a view that shows general status information in Keyguard. -->
 <com.android.keyguard.KeyguardStatusView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
     android:id="@+id/keyguard_status_view"
     android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:gravity="center_horizontal|top">
+    systemui:layout_constraintStart_toStartOf="parent"
+    systemui:layout_constraintEnd_toEndOf="parent"
+    systemui:layout_constraintTop_toTopOf="parent"
+    android:layout_width="0dp"
+    android:layout_height="wrap_content">
     <LinearLayout
         android:id="@+id/status_view_container"
         android:layout_width="match_parent"
@@ -70,5 +72,11 @@
             android:letterSpacing="0.05"
             android:ellipsize="marquee"
             android:singleLine="true" />
+        <FrameLayout
+            android:id="@+id/status_view_media_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:padding="@dimen/qs_media_padding"
+            />
     </LinearLayout>
 </com.android.keyguard.KeyguardStatusView>
diff --git a/packages/SystemUI/res/color/qs_security_footer_background_stroke.xml b/packages/SystemUI/res/color/qs_security_footer_background_stroke.xml
new file mode 100644
index 0000000..aa4d4e8
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_security_footer_background_stroke.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/system_neutral1_100" android:alpha="0.8" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
new file mode 100644
index 0000000..7d370e9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
@@ -0,0 +1,37 @@
+<?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.
+  -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="@dimen/qs_security_footer_background_inset"
+    android:insetBottom="@dimen/qs_security_footer_background_inset"
+    >
+    <ripple
+        android:color="?android:attr/colorControlHighlight">
+        <item android:id="@android:id/mask">
+            <shape android:shape="rectangle">
+                <solid android:color="@android:color/white"/>
+                <corners android:radius="@dimen/qs_security_footer_corner_radius"/>
+            </shape>
+        </item>
+        <item>
+            <shape android:shape="rectangle">
+                <stroke android:width="1dp"
+                        android:color="@color/qs_security_footer_background_stroke"/>
+                <corners android:radius="@dimen/qs_security_footer_corner_radius"/>
+            </shape>
+        </item>
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
index 214c44a..335e0a4 100644
--- a/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_conversation_notification.xml
@@ -26,21 +26,22 @@
 
     <FrameLayout
         android:layout_width="@*android:dimen/conversation_content_start"
-        android:layout_height="25dp"
+        android:layout_height="@dimen/conversation_single_line_face_pile_size"
+        android:paddingHorizontal="16dp"
     >
         <ImageView
             android:id="@*android:id/conversation_icon"
-            android:layout_width="20dp"
-            android:layout_height="20dp"
-            android:layout_gravity="center"
+            android:layout_width="@dimen/conversation_single_line_avatar_size"
+            android:layout_height="@dimen/conversation_single_line_avatar_size"
+            android:layout_gravity="center_vertical|end"
         />
 
         <ViewStub
             android:id="@*android:id/conversation_face_pile"
             android:layout="@*android:layout/conversation_face_pile_layout"
-            android:layout_width="25dp"
-            android:layout_height="25dp"
-            android:layout_gravity="center"
+            android:layout_width="@dimen/conversation_single_line_face_pile_size"
+            android:layout_height="@dimen/conversation_single_line_face_pile_size"
+            android:layout_gravity="center_vertical|end"
         />
     </FrameLayout>
 
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 1f90476..d8bb7e6 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -44,14 +44,6 @@
         android:layout_weight="1"
         />
 
-    <!-- Will hold security footer in landscape with media -->
-    <FrameLayout
-        android:id="@+id/header_text_container"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:gravity="center"
-        />
-
     <include layout="@layout/qs_carrier_group"
         android:id="@+id/carrier_group"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
deleted file mode 100644
index db712e4..0000000
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.systemui.util.NeverExactlyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:minHeight="48dp"
-    android:clickable="true"
-    android:paddingBottom="@dimen/qs_tile_padding_top"
-    android:paddingTop="@dimen/qs_tile_padding_top"
-    android:paddingStart="@dimen/qs_footer_padding_start"
-    android:paddingEnd="@dimen/qs_footer_padding_end"
-    android:gravity="center_vertical"
-    android:layout_gravity="center_vertical|center_horizontal"
-    android:background="@android:color/transparent">
-
-    <ImageView
-        android:id="@+id/primary_footer_icon"
-        android:layout_width="@dimen/qs_footer_icon_size"
-        android:layout_height="@dimen/qs_footer_icon_size"
-        android:gravity="start"
-        android:layout_marginEnd="8dp"
-        android:contentDescription="@null"
-        android:tint="?android:attr/textColorPrimary" />
-
-    <com.android.systemui.util.AutoMarqueeTextView
-        android:id="@+id/footer_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:marqueeRepeatLimit="marquee_forever"
-        android:textAppearance="@style/TextAppearance.QS.TileLabel"
-        android:textColor="?android:attr/textColorPrimary"/>
-
-    <ImageView
-        android:id="@+id/footer_icon"
-        android:layout_width="@dimen/qs_footer_icon_size"
-        android:layout_height="@dimen/qs_footer_icon_size"
-        android:layout_marginStart="8dp"
-        android:contentDescription="@null"
-        android:src="@drawable/ic_info_outline"
-        android:tint="?android:attr/textColorPrimary" />
-
-</com.android.systemui.util.NeverExactlyLinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_security_footer.xml b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
new file mode 100644
index 0000000..de65fa0
--- /dev/null
+++ b/packages/SystemUI/res/layout/quick_settings_security_footer.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.systemui.util.DualHeightHorizontalLinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/qs_security_footer_height"
+    android:clickable="true"
+    android:padding="@dimen/qs_footer_padding"
+    android:gravity="center_vertical"
+    android:layout_gravity="center_vertical|center_horizontal"
+    android:layout_marginVertical="@dimen/qs_security_footer_vertical_margin"
+    android:background="@drawable/qs_security_footer_background"
+    systemui:singleLineHeight="@dimen/qs_security_footer_single_line_height"
+    systemui:textViewId="@id/footer_text"
+    >
+
+    <ImageView
+        android:id="@+id/primary_footer_icon"
+        android:layout_width="@dimen/qs_footer_icon_size"
+        android:layout_height="@dimen/qs_footer_icon_size"
+        android:gravity="start"
+        android:layout_marginEnd="12dp"
+        android:contentDescription="@null"
+        android:tint="?android:attr/textColorSecondary" />
+
+    <TextView
+        android:id="@+id/footer_text"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:maxLines="@integer/qs_security_footer_maxLines"
+        android:ellipsize="end"
+        android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
+        android:textColor="?android:attr/textColorSecondary"/>
+
+    <ImageView
+        android:id="@+id/footer_icon"
+        android:layout_width="@dimen/qs_footer_icon_size"
+        android:layout_height="@dimen/qs_footer_icon_size"
+        android:layout_marginStart="8dp"
+        android:contentDescription="@null"
+        android:src="@*android:drawable/ic_chevron_end"
+        android:tint="?android:attr/textColorSecondary" />
+
+</com.android.systemui.util.DualHeightHorizontalLinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index 22cf2cb..d0e3d3c 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -55,6 +55,16 @@
         android:layout_gravity="center_vertical|center_horizontal"
         android:visibility="gone" />
 
+    <!-- Will hold security footer in landscape with media -->
+    <FrameLayout
+        android:id="@+id/header_text_container"
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
+        android:gravity="center"
+    />
+
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 46a698a..52995ea 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -37,10 +37,6 @@
         android:layout_height="match_parent"
         android:layout_width="match_parent" />
 
-    <include
-        layout="@layout/keyguard_status_view"
-        android:visibility="gone" />
-
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -49,6 +45,10 @@
         android:clipToPadding="false"
         android:clipChildren="false">
 
+        <include
+            layout="@layout/keyguard_status_view"
+            android:visibility="gone"/>
+
         <include layout="@layout/dock_info_overlay" />
 
         <FrameLayout
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
index 50b2f20..e5e8fe6 100644
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -20,7 +20,8 @@
     android:id="@+id/udfps_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    systemui:sensorTouchAreaCoefficient="0.5">
+    systemui:sensorTouchAreaCoefficient="0.5"
+    android:contentDescription="@string/accessibility_fingerprint_label">
 
     <ViewStub
         android:id="@+id/animation_view"
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 5f50fd4..46e7d71 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -30,6 +30,12 @@
     -->
     <dimen name="qs_customize_header_min_height">44dp</dimen>
 
+    <dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen>
+    <dimen name="qs_footer_padding">14dp</dimen>
+    <dimen name="qs_security_footer_vertical_margin">0dp</dimen>
+    <dimen name="qs_security_footer_background_inset">12dp</dimen>
+    <dimen name="qs_security_footer_corner_radius">28dp</dimen>
+
     <dimen name="battery_detail_graph_space_top">9dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
 
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f489fe8..eb72442 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -189,5 +189,11 @@
         <attr name="borderThickness" format="dimension" />
         <attr name="borderColor" format="color" />
     </declare-styleable>
+
+    <declare-styleable name="DualHeightHorizontalLinearLayout">
+        <attr name="singleLineHeight" format="dimension" />
+        <attr name="singleLineVerticalPadding" format="dimension" />
+        <attr name="textViewId" format="reference" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index fff4a1b..f7578cbd 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -605,13 +605,18 @@
     <dimen name="qs_header_carrier_separator_width">6dp</dimen>
     <dimen name="qs_status_separator">32dp</dimen>
     <dimen name="qs_carrier_margin_width">4dp</dimen>
-    <dimen name="qs_footer_padding_start">16dp</dimen>
-    <dimen name="qs_footer_padding_end">16dp</dimen>
-    <dimen name="qs_footer_icon_size">16dp</dimen>
+    <dimen name="qs_footer_icon_size">20dp</dimen>
     <dimen name="qs_paged_tile_layout_padding_bottom">0dp</dimen>
     <dimen name="qs_header_top_padding">15dp</dimen>
     <dimen name="qs_header_bottom_padding">14dp</dimen>
 
+    <dimen name="qs_footer_padding">20dp</dimen>
+    <dimen name="qs_security_footer_height">88dp</dimen>
+    <dimen name="qs_security_footer_single_line_height">48dp</dimen>
+    <dimen name="qs_security_footer_vertical_margin">8dp</dimen>
+    <dimen name="qs_security_footer_background_inset">0dp</dimen>
+    <dimen name="qs_security_footer_corner_radius">28dp</dimen>
+
     <dimen name="qs_notif_collapsed_space">64dp</dimen>
 
     <dimen name="qs_container_bottom_padding">24dp</dimen>
@@ -691,8 +696,11 @@
     <!-- Size of the face pile shown on one-line (children of a group) conversation notifications -->
     <dimen name="conversation_single_line_face_pile_size">25dp</dimen>
 
+    <!-- Size of the avatars within a face pile shown on one-line (children of a group) conversation notifications -->
+    <dimen name="conversation_single_line_face_pile_avatar_size">17dp</dimen>
+
     <!-- Size of an avatar shown on one-line (children of a group) conversation notifications -->
-    <dimen name="conversation_single_line_avatar_size">20dp</dimen>
+    <dimen name="conversation_single_line_avatar_size">24dp</dimen>
 
     <!-- Border width for avatars in the face pile shown on one-line (children of a group) conversation notifications -->
     <dimen name="conversation_single_line_face_pile_protection_width">1dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index b50b5c1..116403c 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -22,6 +22,8 @@
     <integer name="qs_footer_actions_width">0</integer>
     <integer name="qs_footer_actions_weight">1</integer>
 
+    <integer name="qs_security_footer_maxLines">2</integer>
+
     <integer name="magnification_default_scale">2</integer>
 
     <!-- The position of the volume dialog on the screen.
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c117069..a9f6946 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -338,6 +338,8 @@
     <string name="accessibility_voice_assist_button">Voice Assist</string>
     <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_unlock_button">Unlock</string>
+    <!-- Content description of the lock icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_lock_icon">Device locked</string>
     <!-- Content description hint of the unlock button when fingerprint is on (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_waiting_for_fingerprint">Waiting for fingerprint</string>
     <!-- Accessibility action of the unlock button when fingerpint is on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -2958,4 +2960,15 @@
 
     <!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] -->
     <string name="qs_alarm_tile_no_alarm">No alarm set</string>
+
+    <!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] -->
+    <string name="accessibility_fingerprint_label">Fingerprint sensor</string>
+    <!-- Accessibility label for disabled udfps sensor [CHAR LIMIT=NONE] -->
+    <string name="accessibility_udfps_disabled_button">Fingerprint sensor disabled</string>
+    <!-- Accessibility action for tapping on an affordance that will bring up the user's
+    pin/pattern/password bouncer (ie: "Double tap to authenticate") [CHAR LIMIT=NONE] -->
+    <string name="accessibility_authenticate_hint">authenticate</string>
+    <!-- Accessibility action for tapping on an affordance on an unlocked lock screen (ie: "Double
+     tap to enter device") [CHAR LIMIT=NONE] -->
+    <string name="accessibility_enter_hint">enter device</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5da75a1..2e45acc 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -220,6 +220,11 @@
         <item name="android:textSize">@dimen/celltile_rat_type_size</item>
     </style>
 
+    <style name="TextAppearance.QS.SecurityFooter">
+        <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+        <item name="android:textSize">@dimen/qs_tile_text_size</item>
+    </style>
+
     <style name="TextAppearance.QS.Status" parent="TextAppearance.QS.TileLabel.Secondary">
         <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="android:textColor">@color/dark_mode_qs_icon_color_single_tone</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 9df7734..3ab2cca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.CrossFadeHelper;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -44,6 +45,7 @@
  * - keyguard clock
  * - logout button (on certain managed devices)
  * - owner information (if set)
+ * - media player (split shade mode only)
  */
 public class KeyguardStatusView extends GridLayout {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -60,6 +62,7 @@
     private KeyguardSliceView mKeyguardSlice;
     private Runnable mPendingMarqueeStart;
     private Handler mHandler;
+    private View mMediaHostContainer;
 
     private float mDarkAmount = 0;
     private int mTextColor;
@@ -148,6 +151,8 @@
         mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
         onSliceContentChanged();
 
+        mMediaHostContainer = findViewById(R.id.status_view_media_container);
+
         updateOwnerInfo();
         updateDark();
     }
@@ -223,6 +228,9 @@
         }
         mDarkAmount = darkAmount;
         mClockView.setDarkAmount(darkAmount);
+        if (mMediaHostContainer.getVisibility() != View.GONE) {
+            CrossFadeHelper.fadeOut(mMediaHostContainer, darkAmount);
+        }
         updateDark();
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 66ea2ad..9b0ae6b 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -29,9 +29,12 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.Dumpable;
@@ -63,6 +66,7 @@
     @NonNull private final KeyguardStateController mKeyguardStateController;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final AuthController mAuthController;
+    @NonNull private final AccessibilityManager mAccessibilityManager;
 
     private boolean mHasUdfpsOrFaceAuthFeatures;
     private boolean mUdfpsEnrolled;
@@ -93,19 +97,17 @@
             @NonNull KeyguardStateController keyguardStateController,
             @NonNull FalsingManager falsingManager,
             @NonNull AuthController authController,
-            @NonNull DumpManager dumpManager
+            @NonNull DumpManager dumpManager,
+            @NonNull AccessibilityManager accessibilityManager
     ) {
         super(view);
-        if (mView != null) {
-            mView.setOnClickListener(v -> onAffordanceClick());
-            mView.setOnLongClickListener(v -> onAffordanceClick());
-        }
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mAuthController = authController;
         mKeyguardViewController = keyguardViewController;
         mKeyguardStateController = keyguardStateController;
         mFalsingManager = falsingManager;
+        mAccessibilityManager = accessibilityManager;
 
         final Context context = view.getContext();
         mButton = context.getResources().getDrawable(
@@ -122,6 +124,11 @@
     }
 
     @Override
+    protected void onInit() {
+        mView.setAccessibilityDelegate(mAccessibilityDelegate);
+    }
+
+    @Override
     protected void onViewAttached() {
         // we check this here instead of onInit since the FingeprintManager + FaceManager may not
         // have started up yet onInit
@@ -163,6 +170,8 @@
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
         mStatusBarStateController.addCallback(mStatusBarStateListener);
         mKeyguardStateController.addCallback(mKeyguardStateCallback);
+        mAccessibilityManager.addTouchExplorationStateChangeListener(
+                mTouchExplorationStateChangeListener);
 
         updateVisibility();
     }
@@ -172,6 +181,8 @@
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
         mKeyguardStateController.removeCallback(mKeyguardStateCallback);
+        mAccessibilityManager.removeTouchExplorationStateChangeListener(
+                mTouchExplorationStateChangeListener);
     }
 
     public float getTop() {
@@ -210,20 +221,56 @@
         mShowLockIcon = !mUdfpsEnrolled && !mCanDismissLockScreen && isLockScreen()
             && mFaceAuthEnrolled;
 
+        updateClickListener();
         if (mShowButton) {
             mView.setImageDrawable(mButton);
             mView.setVisibility(View.VISIBLE);
+            mView.setContentDescription(getResources().getString(
+                    R.string.accessibility_udfps_disabled_button));
         } else if (mShowUnlockIcon) {
             mView.setImageDrawable(mUnlockIcon);
             mView.setVisibility(View.VISIBLE);
+            mView.setContentDescription(getResources().getString(
+                    R.string.accessibility_unlock_button));
         } else if (mShowLockIcon) {
             mView.setImageDrawable(mLockIcon);
             mView.setVisibility(View.VISIBLE);
+            mView.setContentDescription(getResources().getString(
+                    R.string.accessibility_lock_icon));
         } else {
             mView.setVisibility(View.INVISIBLE);
+            mView.setContentDescription(null);
         }
     }
 
+    private final View.AccessibilityDelegate mAccessibilityDelegate =
+            new View.AccessibilityDelegate() {
+        private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
+                new AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
+                        getResources().getString(R.string.accessibility_authenticate_hint));
+        private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
+                new AccessibilityNodeInfo.AccessibilityAction(
+                        AccessibilityNodeInfoCompat.ACTION_CLICK,
+                        getResources().getString(R.string.accessibility_enter_hint));
+        public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
+            super.onInitializeAccessibilityNodeInfo(v, info);
+            removeAllActions(info);
+            if (mShowButton || mShowLockIcon) {
+                info.addAction(mAccessibilityAuthenticateHint);
+            } else if (mShowUnlockIcon) {
+                info.addAction(mAccessibilityEnterHint);
+            }
+        }
+
+        private void removeAllActions(AccessibilityNodeInfo info) {
+            info.removeAction(mAccessibilityAuthenticateHint);
+            info.removeAction(mAccessibilityEnterHint);
+            info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
+            mView.setLongClickable(false);
+        }
+    };
+
     private boolean isLockScreen() {
         return !mIsDozing
                 && !mIsBouncerShowing
@@ -231,6 +278,17 @@
                 && mStatusBarState == StatusBarState.KEYGUARD;
     }
 
+    private void updateClickListener() {
+        if (mView != null) {
+            mView.setOnClickListener(v -> onAffordanceClick());
+            if (mAccessibilityManager.isTouchExplorationEnabled()) {
+                mView.setOnLongClickListener(null);
+            } else {
+                mView.setOnLongClickListener(v -> onAffordanceClick());
+            }
+        }
+    }
+
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("  mShowBouncerButton: " + mShowButton);
@@ -298,4 +356,7 @@
             updateVisibility();
         }
     };
+
+    private final AccessibilityManager.TouchExplorationStateChangeListener
+            mTouchExplorationStateChangeListener = enabled -> updateClickListener();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 7ebfb72..ee5fb31 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -28,7 +28,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.RectF;
@@ -55,6 +54,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -108,6 +108,7 @@
     @NonNull private final Handler mMainHandler;
     @NonNull private final FalsingManager mFalsingManager;
     @NonNull private final PowerManager mPowerManager;
+    @NonNull private final AccessibilityManager mAccessibilityManager;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -276,6 +277,13 @@
     private final UdfpsView.OnTouchListener mOnTouchListener = (view, event) ->
             onTouch(view, event, true);
 
+    @SuppressLint("ClickableViewAccessibility")
+    private final UdfpsView.OnHoverListener mOnHoverListener = (view, event) ->
+            onTouch(view, event, true);
+
+    private final AccessibilityManager.TouchExplorationStateChangeListener
+            mTouchExplorationStateChangeListener = enabled -> updateTouchListener();
+
     /**
      * @param x coordinate
      * @param y coordinate
@@ -300,6 +308,7 @@
                 udfpsView.onTouchOutsideView();
                 break;
             case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_HOVER_ENTER:
                 // To simplify the lifecycle of the velocity tracker, make sure it's never null
                 // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
                 if (mVelocityTracker == null) {
@@ -322,6 +331,7 @@
                 break;
 
             case MotionEvent.ACTION_MOVE:
+            case MotionEvent.ACTION_HOVER_MOVE:
                 final int idx = mActivePointerId == -1
                         ? event.getPointerId(0)
                         : event.findPointerIndex(mActivePointerId);
@@ -388,6 +398,7 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_HOVER_EXIT:
                 mActivePointerId = -1;
                 if (mVelocityTracker != null) {
                     mVelocityTracker.recycle();
@@ -409,10 +420,9 @@
 
     @Inject
     public UdfpsController(@NonNull Context context,
-            @Main Resources resources,
             @NonNull LayoutInflater inflater,
             @Nullable FingerprintManager fingerprintManager,
-            WindowManager windowManager,
+            @NonNull WindowManager windowManager,
             @NonNull StatusBarStateController statusBarStateController,
             @Main DelayableExecutor fgExecutor,
             @NonNull StatusBar statusBar,
@@ -421,7 +431,8 @@
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
             @NonNull KeyguardViewMediator keyguardViewMediator,
             @NonNull FalsingManager falsingManager,
-            @NonNull PowerManager powerManager) {
+            @NonNull PowerManager powerManager,
+            @NonNull AccessibilityManager accessibilityManager) {
         mContext = context;
         // TODO (b/185124905): inject main handler and vibrator once done prototyping
         mMainHandler = new Handler(Looper.getMainLooper());
@@ -440,6 +451,7 @@
         mKeyguardViewMediator = keyguardViewMediator;
         mFalsingManager = falsingManager;
         mPowerManager = powerManager;
+        mAccessibilityManager = accessibilityManager;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -577,7 +589,9 @@
                     mView.setAnimationViewController(animation);
 
                     mWindowManager.addView(mView, computeLayoutParams(animation));
-                    mView.setOnTouchListener(mOnTouchListener);
+                    mAccessibilityManager.addTouchExplorationStateChangeListener(
+                            mTouchExplorationStateChangeListener);
+                    updateTouchListener();
                 } catch (RuntimeException e) {
                     Log.e(TAG, "showUdfpsOverlay | failed to add window", e);
                 }
@@ -650,7 +664,10 @@
                 onFingerUp();
                 mWindowManager.removeView(mView);
                 mView.setOnTouchListener(null);
+                mView.setOnHoverListener(null);
                 mView.setAnimationViewController(null);
+                mAccessibilityManager.removeTouchExplorationStateChangeListener(
+                        mTouchExplorationStateChangeListener);
                 mView = null;
             } else {
                 Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden");
@@ -758,4 +775,18 @@
                 return defaultEffect;
         }
     }
+
+    private void updateTouchListener() {
+        if (mView == null) {
+            return;
+        }
+
+        if (mAccessibilityManager.isTouchExplorationEnabled()) {
+            mView.setOnHoverListener(mOnHoverListener);
+            mView.setOnTouchListener(null);
+        } else {
+            mView.setOnHoverListener(null);
+            mView.setOnTouchListener(mOnTouchListener);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 9e621b3..2808450 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -197,31 +197,25 @@
     public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         mPriorInteractionType = interactionType;
         if (skipFalsing()) {
+            mPriorResults = getPassedResult(1);
+            logDebug("Skipped falsing");
             return false;
         }
 
-        final boolean booleanResult;
+        final boolean[] localResult = {false};
+        mPriorResults = mClassifiers.stream().map(falsingClassifier -> {
+            FalsingClassifier.Result r = falsingClassifier.classifyGesture(
+                    interactionType,
+                    mHistoryTracker.falseBelief(),
+                    mHistoryTracker.falseConfidence());
+            localResult[0] |= r.isFalse();
 
-        if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
-            final boolean[] localResult = {false};
-            mPriorResults = mClassifiers.stream().map(falsingClassifier -> {
-                FalsingClassifier.Result r = falsingClassifier.classifyGesture(
-                        interactionType,
-                        mHistoryTracker.falseBelief(),
-                        mHistoryTracker.falseConfidence());
-                localResult[0] |= r.isFalse();
+            return r;
+        }).collect(Collectors.toList());
 
-                return r;
-            }).collect(Collectors.toList());
-            booleanResult = localResult[0];
-        } else {
-            booleanResult = false;
-            mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
-        }
+        logDebug("False Gesture: " + localResult[0]);
 
-        logDebug("False Gesture: " + booleanResult);
-
-        return booleanResult;
+        return localResult[0];
     }
 
     @Override
@@ -236,6 +230,8 @@
     @Override
     public boolean isFalseTap(@Penalty int penalty) {
         if (skipFalsing()) {
+            mPriorResults = getPassedResult(1);
+            logDebug("Skipped falsing");
             return false;
         }
 
@@ -264,7 +260,7 @@
         if (!singleTapResult.isFalse()) {
             if (mDataProvider.isJustUnlockedWithFace()) {
                 // Immediately pass if a face is detected.
-                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
+                mPriorResults = getPassedResult(1);
                 logDebug("False Single Tap: false (face detected)");
                 return false;
             } else if (!isFalseDoubleTap()) {
@@ -281,7 +277,7 @@
                 mFalsingTapListeners.forEach(FalsingTapListener::onDoubleTapRequired);
                 return true;
             } else {
-                mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(0.1));
+                mPriorResults = getPassedResult(0.1);
                 logDebug("False Single Tap: false (default)");
                 return false;
             }
@@ -296,6 +292,8 @@
     @Override
     public boolean isFalseDoubleTap() {
         if (skipFalsing()) {
+            mPriorResults = getPassedResult(1);
+            logDebug("Skipped falsing");
             return false;
         }
 
@@ -309,7 +307,10 @@
     }
 
     private boolean skipFalsing() {
-        return !mKeyguardStateController.isShowing();
+        return !mKeyguardStateController.isShowing()
+                || mTestHarness
+                || mDataProvider.isJustUnlockedWithFace()
+                || mDockManager.isDocked();
     }
 
     @Override
@@ -411,6 +412,10 @@
         mHistoryTracker.removeBeliefListener(mBeliefListener);
     }
 
+    private static Collection<FalsingClassifier.Result> getPassedResult(double confidence) {
+        return Collections.singleton(FalsingClassifier.Result.passed(confidence));
+    }
+
     static void logDebug(String msg) {
         logDebug(msg, null);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 6fb8650..2ea139e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.media
 
 import android.view.View
+import android.view.ViewGroup
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.media.dagger.MediaModule.KEYGUARD
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -29,8 +30,8 @@
 import javax.inject.Named
 
 /**
- * A class that controls the media notifications on the lock screen, handles its visibility and
- * is responsible for the embedding of he media experience.
+ * Controls the media notifications on the lock screen, handles its visibility and placement -
+ * switches media player positioning between split pane container vs single pane container
  */
 @SysUISingleton
 class KeyguardMediaController @Inject constructor(
@@ -43,46 +44,114 @@
     init {
         statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
             override fun onStateChanged(newState: Int) {
-                updateVisibility()
+                refreshMediaPosition()
+            }
+
+            override fun onDozingChanged(isDozing: Boolean) {
+                if (!isDozing) {
+                    mediaHost.visible = true
+                    refreshMediaPosition()
+                }
             }
         })
     }
 
     var visibilityChangedListener: ((Boolean) -> Unit)? = null
-    var view: MediaHeaderView? = null
-        private set
 
     /**
-     * Attach this controller to a media view, initializing its state
+     * single pane media container placed at the top of the notifications list
      */
-    fun attach(mediaView: MediaHeaderView) {
-        view = mediaView
+    var singlePaneContainer: MediaHeaderView? = null
+        private set
+    private var splitShadeContainer: ViewGroup? = null
+    private var useSplitShadeContainer: () -> Boolean = { false }
+
+    /**
+     * Attaches media container in single pane mode, situated at the top of the notifications list
+     */
+    fun attachSinglePaneContainer(mediaView: MediaHeaderView?) {
+        singlePaneContainer = mediaView
         // First let's set the desired state that we want for this host
-        mediaHost.addVisibilityChangeListener { updateVisibility() }
-        mediaHost.expansion = 0.0f
+        mediaHost.expansion = MediaHostState.COLLAPSED
         mediaHost.showsOnlyActiveMedia = true
         mediaHost.falsingProtectionNeeded = true
 
         // Let's now initialize this view, which also creates the host view for us.
         mediaHost.init(MediaHierarchyManager.LOCATION_LOCKSCREEN)
-        mediaView.setContentView(mediaHost.hostView)
-
-        // Ensure the visibility is correct
-        updateVisibility()
+        // Required to show it for the first time, afterwards visibility is managed automatically
+        mediaHost.visible = true
+        mediaHost.addVisibilityChangeListener { visible ->
+            refreshMediaPosition()
+            if (visible) {
+                mediaHost.hostView.layoutParams.apply {
+                    height = ViewGroup.LayoutParams.WRAP_CONTENT
+                    width = ViewGroup.LayoutParams.MATCH_PARENT
+                }
+            }
+        }
+        refreshMediaPosition()
     }
 
-    private fun updateVisibility() {
+    /**
+     * Attaches media container in split shade mode, situated to the left of notifications
+     */
+    fun attachSplitShadeContainer(container: ViewGroup, useContainer: () -> Boolean) {
+        splitShadeContainer = container
+        useSplitShadeContainer = useContainer
+    }
+
+    fun refreshMediaPosition() {
         val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD ||
                 statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER)
+        // mediaHost.visible required for proper animations handling
         val shouldBeVisible = mediaHost.visible &&
                 !bypassController.bypassEnabled &&
                 keyguardOrUserSwitcher &&
                 notifLockscreenUserManager.shouldShowLockscreenNotifications()
-        val previousVisibility = view?.visibility ?: View.GONE
-        val newVisibility = if (shouldBeVisible) View.VISIBLE else View.GONE
+        if (shouldBeVisible) {
+            showMediaPlayer()
+        } else {
+            hideMediaPlayer()
+        }
+    }
+
+    private fun showMediaPlayer() {
+        if (useSplitShadeContainer()) {
+            showMediaPlayer(
+                    activeContainer = splitShadeContainer,
+                    inactiveContainer = singlePaneContainer)
+        } else {
+            showMediaPlayer(
+                    activeContainer = singlePaneContainer,
+                    inactiveContainer = splitShadeContainer)
+        }
+    }
+
+    private fun showMediaPlayer(activeContainer: ViewGroup?, inactiveContainer: ViewGroup?) {
+        if (inactiveContainer?.childCount == 1) {
+            inactiveContainer.removeAllViews()
+        }
+        // might be called a few times for the same view, no need to add hostView again
+        if (activeContainer?.childCount == 0) {
+            activeContainer.addView(mediaHost.hostView)
+        }
+        setVisibility(activeContainer, View.VISIBLE)
+        setVisibility(inactiveContainer, View.GONE)
+    }
+
+    private fun hideMediaPlayer() {
+        if (useSplitShadeContainer()) {
+            setVisibility(splitShadeContainer, View.GONE)
+        } else {
+            setVisibility(singlePaneContainer, View.GONE)
+        }
+    }
+
+    private fun setVisibility(view: ViewGroup?, newVisibility: Int) {
+        val previousVisibility = view?.visibility
         view?.visibility = newVisibility
         if (previousVisibility != newVisibility) {
-            visibilityChangedListener?.invoke(shouldBeVisible)
+            visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
         }
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index e6eb317..473db9f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -532,7 +532,10 @@
             mediaCoverImageView.setImageIcon(recommendation.getIcon());
 
             // Set up the click listener if applicable.
-            setSmartspaceRecItemOnClickListener(mediaCoverImageView, recommendation,
+            setSmartspaceRecItemOnClickListener(
+                    mediaCoverImageView,
+                    recommendation,
+                    target.getSmartspaceTargetId(),
                     view -> mMediaDataManagerLazy
                             .get()
                             .dismissSmartspaceRecommendation(0L /* delay */));
@@ -640,6 +643,7 @@
     private void setSmartspaceRecItemOnClickListener(
             @NonNull View view,
             @NonNull SmartspaceAction action,
+            @NonNull String targetId,
             @Nullable View.OnClickListener callback) {
         if (view == null || action == null || action.getIntent() == null) {
             Log.e(TAG, "No tap action can be set up");
@@ -647,6 +651,16 @@
         }
 
         view.setOnClickListener(v -> {
+            // When media recommendation card is shown, there could be only one card.
+            SysUiStatsLog.write(SysUiStatsLog.SMARTSPACE_CARD_REPORTED,
+                    760, // SMARTSPACE_CARD_CLICK
+                    targetId.hashCode(),
+                    SysUiStatsLog
+                            .SMART_SPACE_CARD_REPORTED__CARD_TYPE__HEADPHONE_MEDIA_RECOMMENDATIONS,
+                    getSurfaceForSmartspaceLogging(mMediaViewController.getCurrentEndLocation()),
+                    /* rank */ 1,
+                    /* cardinality */ 1);
+
             mActivityStarter.postStartActivityDismissingKeyguard(
                     action.getIntent(),
                     0 /* delay */,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index 8c12a30..2347481 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -264,15 +264,20 @@
  */
 interface MediaHostState {
 
+    companion object {
+        const val EXPANDED: Float = 1.0f
+        const val COLLAPSED: Float = 0.0f
+    }
+
     /**
-     * The last measurement input that this state was measured with. Infers with and height of
+     * The last measurement input that this state was measured with. Infers width and height of
      * the players.
      */
     var measurementInput: MeasurementInput?
 
     /**
-     * The expansion of the player, 0 for fully collapsed (up to 3 actions), 1 for fully expanded
-     * (up to 5 actions.)
+     * The expansion of the player, [COLLAPSED] for fully collapsed (up to 3 actions),
+     * [EXPANDED] for fully expanded (up to 5 actions).
      */
     var expansion: Float
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index f89e70a..efa1a13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -326,6 +326,7 @@
         super.onConfigurationChanged(newConfig);
         mOnConfigurationChangedListeners.forEach(
                 listener -> listener.onConfigurationChange(newConfig));
+        switchSecurityFooter();
     }
 
     @Override
@@ -364,19 +365,25 @@
         switchToParent((View) newLayout, parent, index);
         index++;
 
-        if (mSecurityFooter != null) {
-            if (mUsingHorizontalLayout && mHeaderContainer != null) {
-                // Adding the security view to the header, that enables us to avoid scrolling
-                switchToParent(mSecurityFooter, mHeaderContainer, 0);
-            } else {
-                switchToParent(mSecurityFooter, parent, index);
-                index++;
-            }
-        }
-
         if (mFooter != null) {
             // Then the footer with the settings
             switchToParent(mFooter, parent, index);
+            index++;
+        }
+
+        // The security footer is switched on orientation changes
+    }
+
+    private void switchSecurityFooter() {
+        if (mSecurityFooter != null) {
+            if (mContext.getResources().getConfiguration().orientation
+                    == Configuration.ORIENTATION_LANDSCAPE && mHeaderContainer != null
+                    && !mSecurityFooter.getParent().equals(mHeaderContainer)) {
+                // Adding the security view to the header, that enables us to avoid scrolling
+                switchToParent(mSecurityFooter, mHeaderContainer, 0);
+            } else {
+                switchToParent(mSecurityFooter, this, -1);
+            }
         }
     }
 
@@ -668,6 +675,7 @@
 
     public void setSecurityFooter(View view) {
         mSecurityFooter = view;
+        switchSecurityFooter();
     }
 
     void setUsingHorizontalLayout(boolean horizontal, ViewGroup mediaHostView, boolean force,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 670475f..baf781d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -16,6 +16,8 @@
 package com.android.systemui.qs;
 
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW;
 
@@ -26,6 +28,8 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.pm.UserInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
@@ -68,7 +72,6 @@
 
     private final View mRootView;
     private final TextView mFooterText;
-    private final ImageView mFooterIcon;
     private final ImageView mPrimaryFooterIcon;
     private final Context mContext;
     private final Callback mCallback = new Callback();
@@ -83,7 +86,6 @@
 
     private boolean mIsVisible;
     private CharSequence mFooterTextContent = null;
-    private int mFooterTextId;
     private int mFooterIconId;
     private Drawable mPrimaryFooterIconDrawable;
 
@@ -94,7 +96,6 @@
         mRootView = rootView;
         mRootView.setOnClickListener(this);
         mFooterText = mRootView.findViewById(R.id.footer_text);
-        mFooterIcon = mRootView.findViewById(R.id.footer_icon);
         mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
         mFooterIconId = R.drawable.ic_info_outline;
         mContext = rootView.getContext();
@@ -120,6 +121,23 @@
 
     public void onConfigurationChanged() {
         FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size);
+
+        Resources r = mContext.getResources();
+
+        mFooterText.setMaxLines(r.getInteger(R.integer.qs_security_footer_maxLines));
+        int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
+        mRootView.setPaddingRelative(padding, padding, padding, padding);
+
+        int verticalMargin = r.getDimensionPixelSize(R.dimen.qs_security_footer_vertical_margin);
+        ViewGroup.MarginLayoutParams lp =
+                (ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
+        lp.topMargin = verticalMargin;
+        lp.bottomMargin = verticalMargin;
+        lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
+                ? MATCH_PARENT : WRAP_CONTENT;
+        mRootView.setLayoutParams(lp);
+
+        mRootView.setBackground(mContext.getDrawable(R.drawable.qs_security_footer_background));
     }
 
     public View getView() {
@@ -189,7 +207,6 @@
         }
         if (mFooterIconId != footerIconId) {
             mFooterIconId = footerIconId;
-            mMainHandler.post(mUpdateIcon);
         }
 
         // Update the primary icon
@@ -315,7 +332,7 @@
         mDialog.setView(createDialogView());
 
         mDialog.show();
-        mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
+        mDialog.getWindow().setLayout(MATCH_PARENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT);
     }
 
@@ -575,19 +592,14 @@
                 == DEVICE_OWNER_TYPE_FINANCED;
     }
 
-    private final Runnable mUpdateIcon = new Runnable() {
-        @Override
-        public void run() {
-            mFooterIcon.setImageResource(mFooterIconId);
-        }
-    };
-
     private final Runnable mUpdatePrimaryIcon = new Runnable() {
         @Override
         public void run() {
-            mPrimaryFooterIcon.setVisibility(mPrimaryFooterIconDrawable != null
-                    ? View.VISIBLE : View.GONE);
-            mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable);
+            if (mPrimaryFooterIconDrawable != null) {
+                mPrimaryFooterIcon.setImageDrawable(mPrimaryFooterIconDrawable);
+            } else {
+                mPrimaryFooterIcon.setImageResource(mFooterIconId);
+            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index b0a9c25..34aac49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -136,7 +136,7 @@
             @QSThemedContext LayoutInflater layoutInflater,
             QSPanel qsPanel
     ) {
-        return layoutInflater.inflate(R.layout.quick_settings_footer, qsPanel, false);
+        return layoutInflater.inflate(R.layout.quick_settings_security_footer, qsPanel, false);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index 9e70f0a..caba3ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -37,8 +37,9 @@
     private ImageView mConversationIconView;
     private TextView mConversationSenderName;
     private View mConversationFacePile;
-    private int mConversationIconSize;
+    private int mSingleAvatarSize;
     private int mFacePileSize;
+    private int mFacePileAvatarSize;
     private int mFacePileProtectionWidth;
 
     public HybridConversationNotificationView(Context context) {
@@ -67,7 +68,9 @@
         mConversationSenderName = requireViewById(R.id.conversation_notification_sender);
         mFacePileSize = getResources()
                 .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_size);
-        mConversationIconSize = getResources()
+        mFacePileAvatarSize = getResources()
+                .getDimensionPixelSize(R.dimen.conversation_single_line_face_pile_avatar_size);
+        mSingleAvatarSize = getResources()
                 .getDimensionPixelSize(R.dimen.conversation_single_line_avatar_size);
         mFacePileProtectionWidth = getResources().getDimensionPixelSize(
                 R.dimen.conversation_single_line_face_pile_protection_width);
@@ -89,6 +92,7 @@
             mConversationFacePile.setVisibility(GONE);
             mConversationIconView.setVisibility(VISIBLE);
             mConversationIconView.setImageIcon(conversationIcon);
+            setSize(mConversationIconView, mSingleAvatarSize);
         } else {
             // If there isn't an icon, generate a "face pile" based on the sender avatars
             mConversationIconView.setVisibility(GONE);
@@ -104,9 +108,9 @@
                     com.android.internal.R.id.conversation_face_pile_top);
             conversationLayout.bindFacePile(facePileBottomBg, facePileBottom, facePileTop);
             setSize(mConversationFacePile, mFacePileSize);
-            setSize(facePileBottom, mConversationIconSize);
-            setSize(facePileTop, mConversationIconSize);
-            setSize(facePileBottomBg, mConversationIconSize + 2 * mFacePileProtectionWidth);
+            setSize(facePileBottom, mFacePileAvatarSize);
+            setSize(facePileTop, mFacePileAvatarSize);
+            setSize(facePileBottomBg, mFacePileAvatarSize + 2 * mFacePileProtectionWidth);
             mTransformationHelper.addViewTransformingToSimilar(facePileTop);
             mTransformationHelper.addViewTransformingToSimilar(facePileBottom);
             mTransformationHelper.addViewTransformingToSimilar(facePileBottomBg);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 884ad8d..fe4ea28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1258,7 +1258,7 @@
                     RemoteInputView riv = RemoteInputView.inflate(
                             mContext, actionContainer, entry, mRemoteInputController);
 
-                    riv.setVisibility(View.INVISIBLE);
+                    riv.setVisibility(View.GONE);
                     actionContainer.addView(riv, new LayoutParams(
                             ViewGroup.LayoutParams.MATCH_PARENT,
                             ViewGroup.LayoutParams.MATCH_PARENT)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
index 15ca24e..8ee9134 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java
@@ -20,14 +20,12 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.util.ArraySet;
 import android.util.Pair;
 import android.view.NotificationHeaderView;
 import android.view.NotificationTopLineView;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewGroup.MarginLayoutParams;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 import android.widget.ImageButton;
@@ -168,53 +166,6 @@
         }
     }
 
-    public void applyConversationSkin() {
-        if (mAppNameText != null) {
-            final ColorStateList colors = mAppNameText.getTextColors();
-            mAppNameText.setTextAppearance(
-                    com.android.internal.R.style
-                            .TextAppearance_DeviceDefault_Notification_Conversation_AppName);
-            mAppNameText.setTextColor(colors);
-            MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
-            layoutParams.setMarginStart(0);
-        }
-        if (mNotificationTopLine != null) {
-            int paddingStart = mNotificationTopLine.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.conversation_content_start);
-            mNotificationTopLine.setPaddingStart(paddingStart);
-        }
-        if (mIcon != null) {
-            MarginLayoutParams layoutParams = (MarginLayoutParams) mIcon.getLayoutParams();
-            int marginStart = mIcon.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.conversation_icon_circle_start);
-            layoutParams.setMarginStart(marginStart);
-        }
-    }
-
-    public void clearConversationSkin() {
-        if (mAppNameText != null) {
-            final ColorStateList colors = mAppNameText.getTextColors();
-            mAppNameText.setTextAppearance(
-                    com.android.internal.R.style.TextAppearance_DeviceDefault_Notification_Info);
-            mAppNameText.setTextColor(colors);
-            MarginLayoutParams layoutParams = (MarginLayoutParams) mAppNameText.getLayoutParams();
-            final int marginStart = mAppNameText.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_header_app_name_margin_start);
-            layoutParams.setMarginStart(marginStart);
-        }
-        if (mNotificationTopLine != null) {
-            int paddingStart = mNotificationTopLine.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_content_margin_start);
-            mNotificationTopLine.setPaddingStart(paddingStart);
-        }
-        if (mIcon != null) {
-            MarginLayoutParams layoutParams = (MarginLayoutParams) mIcon.getLayoutParams();
-            int marginStart = mIcon.getResources().getDimensionPixelSize(
-                    com.android.internal.R.dimen.notification_icon_circle_start);
-            layoutParams.setMarginStart(marginStart);
-        }
-    }
-
     /**
      * Adds the remaining TransformTypes to the TransformHelper. This is done to make sure that each
      * child is faded automatically and doesn't have to be manually added.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
index 040f707..0247a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaHeaderView.java
@@ -19,7 +19,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.ViewGroup;
 
 import com.android.systemui.statusbar.notification.row.ExpandableView;
 
@@ -43,11 +42,4 @@
     public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
         // No animation, it doesn't need it, this would be local
     }
-
-    public void setContentView(ViewGroup contentView) {
-        addView(contentView);
-        ViewGroup.LayoutParams layoutParams = contentView.getLayoutParams();
-        layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 5f3933b..99fe541 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -43,7 +43,6 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.HybridGroupManager;
 import com.android.systemui.statusbar.notification.row.HybridNotificationView;
-import com.android.systemui.statusbar.notification.row.wrapper.NotificationHeaderViewWrapper;
 import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
 
 import java.util.ArrayList;
@@ -336,15 +335,6 @@
         }
         mNotificationHeaderWrapper.setExpanded(mChildrenExpanded);
         mNotificationHeaderWrapper.onContentUpdated(mContainingNotification);
-        if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
-            NotificationHeaderViewWrapper headerWrapper =
-                    (NotificationHeaderViewWrapper) mNotificationHeaderWrapper;
-            if (isConversation) {
-                headerWrapper.applyConversationSkin();
-            } else {
-                headerWrapper.clearConversationSkin();
-            }
-        }
         recreateLowPriorityHeader(builder, isConversation);
         updateHeaderVisibility(false /* animate */);
         updateChildrenAppearance();
@@ -378,15 +368,6 @@
                 header.reapply(getContext(), mNotificationHeaderLowPriority);
             }
             mNotificationHeaderWrapperLowPriority.onContentUpdated(mContainingNotification);
-            if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) {
-                NotificationHeaderViewWrapper headerWrapper =
-                        (NotificationHeaderViewWrapper) mNotificationHeaderWrapper;
-                if (isConversation) {
-                    headerWrapper.applyConversationSkin();
-                } else {
-                    headerWrapper.clearConversationSkin();
-                }
-            }
             resetHeaderVisibilityIfNeeded(mNotificationHeaderLowPriority, calculateDesiredHeader());
         } else {
             removeView(mNotificationHeaderLowPriority);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index b06f7d2..45ce20a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -138,7 +138,7 @@
         incomingHeaderController.reinflateView(parent)
         mediaControlsView =
                 reinflateView(mediaControlsView, layoutInflater, R.layout.keyguard_media_header)
-                        .also(keyguardMediaController::attach)
+        keyguardMediaController.attachSinglePaneContainer(mediaControlsView)
     }
 
     override fun beginsSection(view: View, previous: View?): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 36a370c..527443e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -708,9 +708,10 @@
             mView.setKeyguardMediaControllorVisible(visible);
             if (visible) {
                 mView.generateAddAnimation(
-                        mKeyguardMediaController.getView(), false /*fromMoreCard */);
+                        mKeyguardMediaController.getSinglePaneContainer(),
+                        false /*fromMoreCard */);
             } else {
-                mView.generateRemoveAnimation(mKeyguardMediaController.getView());
+                mView.generateRemoveAnimation(mKeyguardMediaController.getSinglePaneContainer());
             }
             mView.requestChildrenUpdate();
             return Unit.INSTANCE;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 481b2db..069c197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -155,6 +155,8 @@
 
     private int mLockScreenMode;
 
+    private boolean mIsSplitShade;
+
     /**
      * Refreshes the dimension values.
      */
@@ -180,7 +182,7 @@
             int keyguardStatusHeight, int userSwitchHeight, int clockPreferredY,
             int userSwitchPreferredY, boolean hasCustomClock, boolean hasVisibleNotifs, float dark,
             float emptyDragAmount, boolean bypassEnabled, int unlockedStackScrollerPadding,
-            float qsExpansion, int cutoutTopInset) {
+            float qsExpansion, int cutoutTopInset, boolean isSplitShade) {
         mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding,
                 userSwitchHeight);
         mMaxShadeBottom = maxShadeBottom;
@@ -199,6 +201,7 @@
         mUnlockedStackScrollerPadding = unlockedStackScrollerPadding;
         mQsExpansion = qsExpansion;
         mCutoutTopInset = cutoutTopInset;
+        mIsSplitShade = isSplitShade;
     }
 
     public void run(Result result) {
@@ -208,14 +211,23 @@
         result.clockYFullyDozing = getClockY(
                 1.0f /* panelExpansion */, 1.0f /* darkAmount */);
         result.clockAlpha = getClockAlpha(y);
-        result.stackScrollerPadding = mBypassEnabled ? mUnlockedStackScrollerPadding
-                : y + mKeyguardStatusHeight;
+        result.stackScrollerPadding = getStackScrollerPadding(y);
         result.stackScrollerPaddingExpanded = mBypassEnabled ? mUnlockedStackScrollerPadding
                 : getClockY(1.0f, mDarkAmount) + mKeyguardStatusHeight;
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
         result.clockScale = interpolate(getBurnInScale(), 1.0f, 1.0f - mDarkAmount);
     }
 
+    private int getStackScrollerPadding(int clockYPosition) {
+        if (mBypassEnabled) {
+            return mUnlockedStackScrollerPadding;
+        } else if (mIsSplitShade) {
+            return clockYPosition;
+        } else {
+            return clockYPosition + mKeyguardStatusHeight;
+        }
+    }
+
     /**
      * Update lock screen mode for testing different layouts
      */
@@ -232,15 +244,11 @@
         return mHeight / 2 - mKeyguardStatusHeight - mClockNotificationsMargin;
     }
 
-    private int getPreferredClockY() {
-        return mClockPreferredY;
-    }
-
     private int getExpandedPreferredClockY() {
         if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) {
             return mMinTopMargin + mUserSwitchHeight;
         }
-        return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? getPreferredClockY()
+        return (mHasCustomClock && (!mHasVisibleNotifs || mBypassEnabled)) ? mClockPreferredY
                 : getExpandedClockPosition();
     }
 
@@ -271,7 +279,7 @@
 
     private int getClockY(float panelExpansion, float darkAmount) {
         // Dark: Align the bottom edge of the clock at about half of the screen:
-        float clockYDark = (mHasCustomClock ? getPreferredClockY() : getMaxClockY())
+        float clockYDark = (mHasCustomClock ? mClockPreferredY : getMaxClockY())
                 + burnInPreventionOffsetY();
         clockYDark = MathUtils.max(0, clockYDark);
 
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 d85be1d..8574830 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -28,6 +28,7 @@
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade;
 
 import static java.lang.Float.isNaN;
 
@@ -99,6 +100,7 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
+import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.plugins.FalsingManager;
@@ -527,6 +529,7 @@
 
     private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
     private int mScrimCornerRadius;
+    private KeyguardMediaController mKeyguardMediaController;
 
     private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
         @Override
@@ -597,6 +600,7 @@
             LockIconViewController lockIconViewController,
             FeatureFlags featureFlags,
             QuickAccessWalletClient quickAccessWalletClient,
+            KeyguardMediaController keyguardMediaController,
             @Main Executor uiExecutor) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
@@ -604,6 +608,7 @@
                 statusBarTouchableRegionManager, ambientState);
         mView = view;
         mVibratorHelper = vibratorHelper;
+        mKeyguardMediaController = keyguardMediaController;
         mMetricsLogger = metricsLogger;
         mActivityManager = activityManager;
         mConfigurationController = configurationController;
@@ -771,7 +776,6 @@
         });
 
         mView.setAccessibilityDelegate(mAccessibilityDelegate);
-        // dynamically apply the split shade value overrides.
         if (mShouldUseSplitNotificationShade) {
             updateResources();
         }
@@ -889,13 +893,17 @@
             constraintSet.connect(
                     R.id.notification_stack_scroller, START,
                     R.id.qs_edge_guideline, START);
+            constraintSet.connect(R.id.keyguard_status_view, END, R.id.qs_edge_guideline, END);
         } else {
             constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END);
             constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START);
+            constraintSet.connect(R.id.keyguard_status_view, END, PARENT_ID, END);
         }
         constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
         constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
         constraintSet.applyTo(mNotificationContainerParent);
+
+        mKeyguardMediaController.refreshMediaPosition();
     }
 
     private static void ensureAllViewsHaveIds(ViewGroup parentView) {
@@ -929,12 +937,18 @@
     private void reInflateViews() {
         if (DEBUG) Log.d(TAG, "reInflateViews");
         // Re-inflate the status view group.
-        KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
-        int index = mView.indexOfChild(keyguardStatusView);
-        mView.removeView(keyguardStatusView);
+        KeyguardStatusView keyguardStatusView =
+                mNotificationContainerParent.findViewById(R.id.keyguard_status_view);
+        int statusIndex = mNotificationContainerParent.indexOfChild(keyguardStatusView);
+        mNotificationContainerParent.removeView(keyguardStatusView);
         keyguardStatusView = (KeyguardStatusView) mLayoutInflater.inflate(
-                R.layout.keyguard_status_view, mView, false);
-        mView.addView(keyguardStatusView, index);
+                R.layout.keyguard_status_view, mNotificationContainerParent, false);
+        mNotificationContainerParent.addView(keyguardStatusView, statusIndex);
+        attachSplitShadeMediaPlayerContainer(
+                keyguardStatusView.findViewById(R.id.status_view_media_container));
+
+        // we need to update KeyguardStatusView constraints after reinflating it
+        updateResources();
 
         // Re-inflate the keyguard user switcher group.
         boolean isUserSwitcherEnabled = mUserManager.isUserSwitcherEnabled();
@@ -956,11 +970,11 @@
                         showKeyguardUserSwitcher /* enabled */);
 
         mBigClockContainer.removeAllViews();
-        updateViewControllers(
-                keyguardStatusView, userAvatarView, mKeyguardStatusBar, keyguardUserSwitcherView);
+        updateViewControllers(mView.findViewById(R.id.keyguard_status_view), userAvatarView,
+                mKeyguardStatusBar, keyguardUserSwitcherView);
 
         // Update keyguard bottom area
-        index = mView.indexOfChild(mKeyguardBottomArea);
+        int index = mView.indexOfChild(mKeyguardBottomArea);
         mView.removeView(mKeyguardBottomArea);
         KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
         mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate(
@@ -998,6 +1012,11 @@
         setKeyguardBottomAreaVisibility(mBarState, false);
     }
 
+    private void attachSplitShadeMediaPlayerContainer(FrameLayout container) {
+        mKeyguardMediaController.attachSplitShadeContainer(container,
+                () -> mShouldUseSplitNotificationShade);
+    }
+
     private void initBottomArea() {
         mAffordanceHelper = new KeyguardAffordanceHelper(
                 mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
@@ -1110,7 +1129,8 @@
                     hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount,
                     bypassEnabled, getUnlockedStackScrollerPadding(),
                     getQsExpansionFraction(),
-                    mDisplayCutoutTopInset);
+                    mDisplayCutoutTopInset,
+                    shouldUseSplitNotificationShade(mFeatureFlags, mResources));
             mClockPositionAlgorithm.run(mClockPositionResult);
             mKeyguardStatusViewController.updatePosition(
                     mClockPositionResult.clockX, mClockPositionResult.clockY,
diff --git a/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt
new file mode 100644
index 0000000..0e04871
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.AttributeSet
+import android.util.DisplayMetrics
+import android.util.TypedValue
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.R
+
+/**
+ * Horizontal [LinearLayout] to contain some text.
+ *
+ * The height of this container can alternate between two different heights, depending on whether
+ * the text takes one line or more.
+ *
+ * When the text takes multiple lines, it will use the values in the regular attributes (`padding`,
+ * `layout_height`). The single line behavior must be set in XML.
+ *
+ * XML attributes for single line behavior:
+ * * `systemui:textViewId`: set the id for the [TextView] that determines the height of the
+ *   container
+ * * `systemui:singleLineHeight`: sets the height of the view when the text takes up only one line.
+ *   By default, it will use [getMinimumHeight].
+ * * `systemui:singleLineVerticalPadding`: sets the padding (top and bottom) when then text takes up
+ * only one line. By default, it is 0.
+ *
+ * All dimensions are updated when configuration changes.
+ */
+class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAttrs: Int = 0,
+    defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
+
+    private val singleLineHeightValue: TypedValue?
+    private var singleLineHeightPx = 0
+
+    private val singleLineVerticalPaddingValue: TypedValue?
+    private var singleLineVerticalPaddingPx = 0
+
+    private val textViewId: Int
+    private var textView: TextView? = null
+
+    private val displayMetrics: DisplayMetrics
+        get() = context.resources.displayMetrics
+
+    private var initialPadding = mPaddingTop // All vertical padding is the same
+
+    init {
+        if (orientation != HORIZONTAL) {
+            throw IllegalStateException("This view should always have horizontal orientation")
+        }
+
+        val ta = context.obtainStyledAttributes(
+                attrs,
+                R.styleable.DualHeightHorizontalLinearLayout, defStyleAttrs, defStyleRes
+        )
+
+        val tempHeight = TypedValue()
+        singleLineHeightValue = if (
+                ta.hasValue(R.styleable.DualHeightHorizontalLinearLayout_singleLineHeight)
+        ) {
+            ta.getValue(R.styleable.DualHeightHorizontalLinearLayout_singleLineHeight, tempHeight)
+            tempHeight
+        } else {
+            null
+        }
+
+        val tempPadding = TypedValue()
+        singleLineVerticalPaddingValue = if (
+                ta.hasValue(R.styleable.DualHeightHorizontalLinearLayout_singleLineVerticalPadding)
+        ) {
+            ta.getValue(
+                    R.styleable.DualHeightHorizontalLinearLayout_singleLineVerticalPadding,
+                    tempPadding
+            )
+            tempPadding
+        } else {
+            null
+        }
+
+        textViewId = ta.getResourceId(R.styleable.DualHeightHorizontalLinearLayout_textViewId, 0)
+
+        ta.recycle()
+    }
+
+    init {
+        updateResources()
+    }
+
+    override fun setPadding(left: Int, top: Int, right: Int, bottom: Int) {
+        super.setPadding(left, top, right, bottom)
+        initialPadding = top
+    }
+
+    override fun setPaddingRelative(start: Int, top: Int, end: Int, bottom: Int) {
+        super.setPaddingRelative(start, top, end, bottom)
+        initialPadding = top
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        textView?.let { tv ->
+            if (tv.lineCount < 2) {
+                setMeasuredDimension(measuredWidth, singleLineHeightPx)
+                mPaddingBottom = 0
+                mPaddingTop = 0
+            } else {
+                mPaddingBottom = initialPadding
+                mPaddingTop = initialPadding
+            }
+        }
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        textView = findViewById(textViewId)
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration?) {
+        super.onConfigurationChanged(newConfig)
+        updateResources()
+    }
+
+    override fun setOrientation(orientation: Int) {
+        if (orientation == VERTICAL) {
+            throw IllegalStateException("This view should always have horizontal orientation")
+        }
+        super.setOrientation(orientation)
+    }
+
+    private fun updateResources() {
+        updateDimensionValue(singleLineHeightValue, minimumHeight, ::singleLineHeightPx::set)
+        updateDimensionValue(singleLineVerticalPaddingValue, 0, ::singleLineVerticalPaddingPx::set)
+    }
+
+    private inline fun updateDimensionValue(
+        tv: TypedValue?,
+        defaultValue: Int,
+        propertySetter: (Int) -> Unit
+    ) {
+        val value = tv?.let {
+            if (it.resourceId != 0) {
+                context.resources.getDimensionPixelSize(it.resourceId)
+            } else {
+                it.getDimension(displayMetrics).toInt()
+            }
+        } ?: defaultValue
+        propertySetter(value)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 725f0e6..40c4851 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -24,7 +24,6 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.biometrics.ComponentInfoInternal;
 import android.hardware.biometrics.SensorProperties;
@@ -40,6 +39,7 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.filters.SmallTest;
 
@@ -85,8 +85,6 @@
 
     // Dependencies
     @Mock
-    private Resources mResources;
-    @Mock
     private LayoutInflater mLayoutInflater;
     @Mock
     private FingerprintManager mFingerprintManager;
@@ -110,6 +108,8 @@
     private FalsingManager mFalsingManager;
     @Mock
     private PowerManager mPowerManager;
+    @Mock
+    private AccessibilityManager mAccessibilityManager;
 
     private FakeExecutor mFgExecutor;
 
@@ -151,7 +151,6 @@
         mFgExecutor = new FakeExecutor(new FakeSystemClock());
         mUdfpsController = new UdfpsController(
                 mContext,
-                mResources,
                 mLayoutInflater,
                 mFingerprintManager,
                 mWindowManager,
@@ -163,7 +162,8 @@
                 mKeyguardUpdateMonitor,
                 mKeyguardViewMediator,
                 mFalsingManager,
-                mPowerManager);
+                mPowerManager,
+                mAccessibilityManager);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
 
@@ -174,16 +174,9 @@
         when(mBrightnessValues.length()).thenReturn(2);
         when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
         when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
-        when(mResources.obtainTypedArray(com.android.internal.R.array.config_screenBrightnessNits))
-                .thenReturn(mBrightnessValues);
         when(mBrightnessBacklight.length()).thenReturn(2);
         when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
         when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
-        when(mResources.obtainTypedArray(
-                com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
-                .thenReturn(mBrightnessBacklight);
-        when(mResources.getIntArray(com.android.internal.R.array.config_screenBrightnessBacklight))
-                .thenReturn(new int[]{1, 2});
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 730c941..0d67d66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -19,26 +19,23 @@
 import android.testing.AndroidTestingRunner
 import android.view.View.GONE
 import android.view.View.VISIBLE
+import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.stack.MediaHeaderView
 import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.animation.UniqueObjectHostView
+import com.google.common.truth.Truth.assertThat
 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.Mockito.`when`
-import org.mockito.Mockito.atLeastOnce
-import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -52,51 +49,81 @@
     private lateinit var statusBarStateController: SysuiStatusBarStateController
     @Mock
     private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
-    @Mock
-    private lateinit var mediaHeaderView: MediaHeaderView
-    @Captor
-    private lateinit var visibilityListener: ArgumentCaptor<((Boolean) -> Unit)>
     @JvmField @Rule
     val mockito = MockitoJUnit.rule()
+
+    private val mediaHeaderView: MediaHeaderView = MediaHeaderView(context, null)
     private lateinit var keyguardMediaController: KeyguardMediaController
 
     @Before
     fun setup() {
+        // default state is positive, media should show up
+        whenever(mediaHost.visible).thenReturn(true)
+        whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+        whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
+                .thenReturn(true)
+        whenever(mediaHost.hostView).thenReturn(UniqueObjectHostView(context))
+
         keyguardMediaController = KeyguardMediaController(mediaHost, bypassController,
                 statusBarStateController, notificationLockscreenUserManager)
+        keyguardMediaController.attachSinglePaneContainer(mediaHeaderView)
     }
 
     @Test
-    fun testAttach_hiddenWhenHostIsHidden() {
-        `when`(mediaHost.visible).thenReturn(false)
-        triggerVisibilityListener()
+    fun testHiddenWhenHostIsHidden() {
+        whenever(mediaHost.visible).thenReturn(false)
 
-        verify(mediaHeaderView, atLeastOnce()).visibility = eq(GONE)
-    }
-    @Test
-    fun testAttach_visibleOnKeyguard() {
-        `when`(mediaHost.visible).thenReturn(true)
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
-                .thenReturn(true)
-        triggerVisibilityListener()
+        keyguardMediaController.refreshMediaPosition()
 
-        verify(mediaHeaderView, atLeastOnce()).visibility = eq(VISIBLE)
+        assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
     }
+
     @Test
-    fun testAttach_hiddenOnKeyguard_whenNotificationsAreHidden() {
-        `when`(mediaHost.visible).thenReturn(true)
-        `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
-        `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
+    fun testVisibleOnKeyguardOrFullScreenUserSwitcher() {
+        testStateVisibility(StatusBarState.SHADE, GONE)
+        testStateVisibility(StatusBarState.SHADE_LOCKED, GONE)
+        testStateVisibility(StatusBarState.FULLSCREEN_USER_SWITCHER, VISIBLE)
+        testStateVisibility(StatusBarState.KEYGUARD, VISIBLE)
+    }
+
+    private fun testStateVisibility(state: Int, visibility: Int) {
+        whenever(statusBarStateController.state).thenReturn(state)
+        keyguardMediaController.refreshMediaPosition()
+        assertThat(mediaHeaderView.visibility).isEqualTo(visibility)
+    }
+
+    @Test
+    fun testHiddenOnKeyguard_whenNotificationsAreHidden() {
+        whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications())
                 .thenReturn(false)
-        triggerVisibilityListener()
 
-        verify(mediaHeaderView, atLeastOnce()).visibility = eq(GONE)
+        keyguardMediaController.refreshMediaPosition()
+
+        assertThat(mediaHeaderView.visibility).isEqualTo(GONE)
     }
 
-    private fun triggerVisibilityListener() {
-        keyguardMediaController.attach(mediaHeaderView)
-        verify(mediaHost).addVisibilityChangeListener(capture(visibilityListener))
-        visibilityListener.value.invoke(true)
+    @Test
+    fun testActivatesSplitShadeContainerInSplitShadeMode() {
+        val splitShadeContainer = FrameLayout(context)
+        keyguardMediaController.attachSplitShadeContainer(
+                splitShadeContainer,
+                useContainer = { true })
+
+        keyguardMediaController.refreshMediaPosition()
+
+        assertThat(splitShadeContainer.visibility).isEqualTo(VISIBLE)
     }
-}
\ No newline at end of file
+
+    @Test
+    fun testActivatesSinglePaneContainerInSinglePaneMode() {
+        val splitShadeContainer = FrameLayout(context)
+        keyguardMediaController.attachSplitShadeContainer(
+                splitShadeContainer,
+                useContainer = { false })
+
+        keyguardMediaController.refreshMediaPosition()
+
+        assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
+        assertThat(mediaHeaderView.visibility).isEqualTo(VISIBLE)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 4ee639b..234dec2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -81,10 +81,10 @@
     private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App";
     private static final ComponentName DEVICE_OWNER_COMPONENT =
             new ComponentName("TestDPC", "Test");
+    private static final int DEFAULT_ICON_ID = R.drawable.ic_info_outline;
 
     private ViewGroup mRootView;
     private TextView mFooterText;
-    private TestableImageView mFooterIcon;
     private TestableImageView mPrimaryFooterIcon;
     private QSSecurityFooter mFooter;
     @Mock
@@ -101,11 +101,10 @@
         when(mUserTracker.getUserInfo()).thenReturn(mock(UserInfo.class));
         mRootView = (ViewGroup) new LayoutInflaterBuilder(mContext)
                 .replace("ImageView", TestableImageView.class)
-                .build().inflate(R.layout.quick_settings_footer, null, false);
+                .build().inflate(R.layout.quick_settings_security_footer, null, false);
         mFooter = new QSSecurityFooter(mRootView, mUserTracker, new Handler(looper),
                 mActivityStarter, mSecurityController, looper);
         mFooterText = mRootView.findViewById(R.id.footer_text);
-        mFooterIcon = mRootView.findViewById(R.id.footer_icon);
         mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
         mFooter.setHostEnvironment(null);
 
@@ -135,10 +134,8 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management),
                      mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        // -1 == never set.
-        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
     }
 
     @Test
@@ -153,10 +150,8 @@
                                         MANAGING_ORGANIZATION),
                 mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        // -1 == never set.
-        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
     }
 
     @Test
@@ -174,10 +169,8 @@
                 R.string.quick_settings_financed_disclosure_named_management,
                 MANAGING_ORGANIZATION), mFooterText.getText());
         assertEquals(View.VISIBLE, mRootView.getVisibility());
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        // -1 == never set.
-        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
     }
 
     @Test
@@ -204,10 +197,8 @@
         TestableLooper.get(this).processAllMessages();
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        // -1 == never set.
-        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
         when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -255,9 +246,8 @@
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_named_vpn,
                                         VPN_PACKAGE),
                      mFooterText.getText());
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
         when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -282,9 +272,8 @@
         TestableLooper.get(this).processAllMessages();
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
                      mFooterText.getText());
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
 
         // Same situation, but with organization name set
         when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -306,9 +295,8 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
-        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
+        assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
                 mFooterText.getText());
     }
@@ -320,8 +308,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        // -1 == never set.
-        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(
                              R.string.quick_settings_disclosure_managed_profile_monitoring),
                      mFooterText.getText());
@@ -345,8 +332,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        // -1 == never set.
-        assertEquals(-1, mFooterIcon.getLastImageResource());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_monitoring),
                      mFooterText.getText());
     }
@@ -359,7 +345,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns),
                      mFooterText.getText());
     }
@@ -371,7 +357,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(
                              R.string.quick_settings_disclosure_managed_profile_named_vpn,
                              VPN_PACKAGE_2),
@@ -412,7 +398,7 @@
         mFooter.refreshState();
 
         TestableLooper.get(this).processAllMessages();
-        assertEquals(R.drawable.stat_sys_vpn_ic, mFooterIcon.getLastImageResource());
+        assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
         assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn,
                                         VPN_PACKAGE),
                      mFooterText.getText());
@@ -648,12 +634,12 @@
 
         assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable());
 
-        // Ensure the primary icon is set to gone when parental controls is disabled.
+        // Ensure the primary icon is back to default after parental controls are gone
         when(mSecurityController.isParentalControlsEnabled()).thenReturn(false);
         mFooter.refreshState();
         TestableLooper.get(this).processAllMessages();
 
-        assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
+        assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 02e2e4c..152ba90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -53,6 +53,7 @@
     private boolean mHasVisibleNotifs;
     private float mQsExpansion;
     private int mCutoutTopInset = 0; // in pixels
+    private boolean mIsSplitShade = false;
 
     @Before
     public void setUp() {
@@ -265,6 +266,19 @@
     }
 
     @Test
+    public void notifPositionAlignedWithClockInSplitShadeMode() {
+        // GIVEN on lock screen and split shade mode
+        givenLockScreen();
+        mIsSplitShade = true;
+        mPreferredClockY = 100;
+        mHasCustomClock = true;
+        // WHEN the position algorithm is run
+        positionClock();
+        // THEN the notif padding DOESN'T adjust for keyguard status height.
+        assertThat(mClockPosition.stackScrollerPadding).isEqualTo(mPreferredClockY);
+    }
+
+    @Test
     public void notifPositionWithLargeClockOnLockScreen() {
         // GIVEN on lock screen and clock has a nonzero height
         givenLockScreen();
@@ -397,7 +411,7 @@
                 0 /* userSwitchHeight */, mPreferredClockY, 0 /* userSwitchPreferredY */,
                 mHasCustomClock, mHasVisibleNotifs, mDark, ZERO_DRAG, false /* bypassEnabled */,
                 0 /* unlockedStackScrollerPadding */, mQsExpansion,
-                mCutoutTopInset);
+                mCutoutTopInset, mIsSplitShade);
         mClockPositionAlgorithm.run(mClockPosition);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 2e4d253..4bac762 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -75,6 +75,7 @@
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.doze.DozeLog;
+import com.android.systemui.media.KeyguardMediaController;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.media.MediaHierarchyManager;
 import com.android.systemui.qs.QSDetailDisplayer;
@@ -240,6 +241,8 @@
     private LockIconViewController mLockIconViewController;
     @Mock
     private QuickAccessWalletClient mQuickAccessWalletClient;
+    @Mock
+    private KeyguardMediaController mKeyguardMediaController;
 
     private SysuiStatusBarStateController mStatusBarStateController;
     private NotificationPanelViewController mNotificationPanelViewController;
@@ -282,6 +285,7 @@
         mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null);
         mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame));
         mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller));
+        mNotificationContainerParent.addView(newViewWithId(R.id.keyguard_status_view));
         when(mView.findViewById(R.id.notification_container_parent))
                 .thenReturn(mNotificationContainerParent);
         FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
@@ -346,6 +350,7 @@
                 mLockIconViewController,
                 mFeatureFlags,
                 mQuickAccessWalletClient,
+                mKeyguardMediaController,
                 new FakeExecutor(new FakeSystemClock()));
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
@@ -476,6 +481,20 @@
     }
 
     @Test
+    public void testKeyguardStatusView_isAlignedToGuidelineInSplitShadeMode() {
+        mNotificationPanelViewController.updateResources();
+
+        assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
+                .isEqualTo(ConstraintSet.PARENT_ID);
+
+        enableSplitShade();
+        mNotificationPanelViewController.updateResources();
+
+        assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd)
+                .isEqualTo(R.id.qs_edge_guideline);
+    }
+
+    @Test
     public void testSplitShadeLayout_isAlignedToGuideline() {
         enableSplitShade();
 
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 6ca3c4b..8f60b09 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -52,7 +52,6 @@
 import android.net.NetworkScore;
 import android.net.RouteInfo;
 import android.net.TelephonyNetworkSpecifier;
-import android.net.TunnelConnectionParams;
 import android.net.Uri;
 import android.net.annotations.PolicyDirection;
 import android.net.ipsec.ike.ChildSessionCallback;
@@ -1924,14 +1923,8 @@
             @NonNull IpSecTunnelInterface tunnelIface,
             @NonNull VcnChildSessionConfiguration childConfig,
             @Nullable UnderlyingNetworkRecord underlying) {
-        final TunnelConnectionParams tunnelParams =
+        final IkeTunnelConnectionParams ikeTunnelParams =
                 gatewayConnectionConfig.getTunnelConnectionParams();
-        if (!(tunnelParams instanceof IkeTunnelConnectionParams)) {
-            throw new IllegalStateException(
-                    "TunnelConnectionParams is not IkeTunnelConnectionParams");
-        }
-
-        final IkeTunnelConnectionParams ikeTunnelParams = (IkeTunnelConnectionParams) tunnelParams;
         final LinkProperties lp = new LinkProperties();
 
         lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -2138,32 +2131,16 @@
     }
 
     private IkeSessionParams buildIkeParams(@NonNull Network network) {
-        final TunnelConnectionParams tunnelConnectionParams =
+        final IkeTunnelConnectionParams ikeTunnelConnectionParams =
                 mConnectionConfig.getTunnelConnectionParams();
-
-        if (tunnelConnectionParams instanceof IkeTunnelConnectionParams) {
-            final IkeTunnelConnectionParams ikeTunnelConnectionParams =
-                    (IkeTunnelConnectionParams) tunnelConnectionParams;
-            final IkeSessionParams.Builder builder =
-                    new IkeSessionParams.Builder(ikeTunnelConnectionParams.getIkeSessionParams());
-            builder.setNetwork(network);
-
-            return builder.build();
-        }
-
-        throw new IllegalStateException("TunnelConnectionParams is not IkeTunnelConnectionParams");
+        final IkeSessionParams.Builder builder =
+                new IkeSessionParams.Builder(ikeTunnelConnectionParams.getIkeSessionParams());
+        builder.setNetwork(network);
+        return builder.build();
     }
 
     private ChildSessionParams buildChildParams() {
-        final TunnelConnectionParams tunnelConnectionParams =
-                mConnectionConfig.getTunnelConnectionParams();
-
-        if (tunnelConnectionParams instanceof IkeTunnelConnectionParams) {
-            return ((IkeTunnelConnectionParams) tunnelConnectionParams)
-                    .getTunnelModeChildSessionParams();
-        }
-
-        throw new IllegalStateException("TunnelConnectionParams is not IkeTunnelConnectionParams");
+        return mConnectionConfig.getTunnelConnectionParams().getTunnelModeChildSessionParams();
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index ca01d0f..aa2e6b9 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -34,6 +34,7 @@
 import android.telephony.ims.stub.DelegateConnectionMessageCallback;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
 import android.telephony.ims.stub.SipDelegate;
+import android.util.ArrayMap;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -163,6 +164,35 @@
     })
     public @interface MessageFailureReason {}
 
+    /**@hide*/
+    public static final ArrayMap<Integer, String> MESSAGE_FAILURE_REASON_STRING_MAP =
+            new ArrayMap<>(11);
+    static {
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_UNKNOWN,
+                "MESSAGE_FAILURE_REASON_UNKNOWN");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_DELEGATE_DEAD,
+                "MESSAGE_FAILURE_REASON_DELEGATE_DEAD");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_DELEGATE_CLOSED,
+                "MESSAGE_FAILURE_REASON_DELEGATE_CLOSED");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS,
+                "MESSAGE_FAILURE_REASON_INVALID_HEADER_FIELDS");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT,
+                "MESSAGE_FAILURE_REASON_INVALID_BODY_CONTENT");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG,
+                "MESSAGE_FAILURE_REASON_INVALID_FEATURE_TAG");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(
+                MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE,
+                "MESSAGE_FAILURE_REASON_TAG_NOT_ENABLED_FOR_DELEGATE");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE,
+                "MESSAGE_FAILURE_REASON_NETWORK_NOT_AVAILABLE");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_NOT_REGISTERED,
+                "MESSAGE_FAILURE_REASON_NOT_REGISTERED");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION,
+                "MESSAGE_FAILURE_REASON_STALE_IMS_CONFIGURATION");
+        MESSAGE_FAILURE_REASON_STRING_MAP.append(
+                MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION,
+                "MESSAGE_FAILURE_REASON_INTERNAL_DELEGATE_STATE_TRANSITION");
+    }
 
     /**
      * Access to use this feature tag has been denied for an unknown reason.
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 0d3fd3f..7cfd275 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -22,7 +22,7 @@
 import static org.junit.Assert.fail;
 
 import android.net.NetworkCapabilities;
-import android.net.TunnelConnectionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
 
 import androidx.test.filters.SmallTest;
@@ -60,7 +60,7 @@
             };
     public static final int MAX_MTU = 1360;
 
-    public static final TunnelConnectionParams TUNNEL_CONNECTION_PARAMS =
+    public static final IkeTunnelConnectionParams TUNNEL_CONNECTION_PARAMS =
             TunnelConnectionParamsUtilsTest.buildTestParams();
 
     public static final String GATEWAY_CONNECTION_NAME_PREFIX = "gatewayConnectionName-";
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 530e636..90ee738 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -51,7 +51,6 @@
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.ipsec.ike.ChildSaProposal;
-import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -181,7 +180,7 @@
         assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
 
         final List<ChildSaProposal> saProposals =
-                ((IkeTunnelConnectionParams) mConfig.getTunnelConnectionParams())
+                mConfig.getTunnelConnectionParams()
                         .getTunnelModeChildSessionParams()
                         .getSaProposals();
         final int expectedMtu =