Merge changes Ia5accb74,Ic4d7def4,I30bd56fc into sc-v2-dev

* changes:
  Change DialogLaunchAnimator X-axis interpolator
  Extract interpolators and durations out of LaunchAnimator
  Synchronize dialog launch animation using BLAST
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index fca4c69..3712caed 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1488,18 +1488,27 @@
                     mContext.getUserId());
             if (fd != null) {
                 FileOutputStream fos = null;
-                boolean ok = false;
+                final Bitmap tmp = BitmapFactory.decodeStream(resources.openRawResource(resid));
                 try {
-                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
-                    copyStreamToWallpaperFile(resources.openRawResource(resid), fos);
-                    // The 'close()' is the trigger for any server-side image manipulation,
-                    // so we must do that before waiting for completion.
-                    fos.close();
-                    completion.waitForCompletion();
+                    // If the stream can't be decoded, treat it as an invalid input.
+                    if (tmp != null) {
+                        fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+                        tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
+                        // The 'close()' is the trigger for any server-side image manipulation,
+                        // so we must do that before waiting for completion.
+                        fos.close();
+                        completion.waitForCompletion();
+                    } else {
+                        throw new IllegalArgumentException(
+                                "Resource 0x" + Integer.toHexString(resid) + " is invalid");
+                    }
                 } finally {
                     // Might be redundant but completion shouldn't wait unless the write
                     // succeeded; this is a fallback if it threw past the close+wait.
                     IoUtils.closeQuietly(fos);
+                    if (tmp != null) {
+                        tmp.recycle();
+                    }
                 }
             }
         } catch (RemoteException e) {
@@ -1741,13 +1750,22 @@
                     result, which, completion, mContext.getUserId());
             if (fd != null) {
                 FileOutputStream fos = null;
+                final Bitmap tmp = BitmapFactory.decodeStream(bitmapData);
                 try {
-                    fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
-                    copyStreamToWallpaperFile(bitmapData, fos);
-                    fos.close();
-                    completion.waitForCompletion();
+                    // If the stream can't be decoded, treat it as an invalid input.
+                    if (tmp != null) {
+                        fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
+                        tmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
+                        fos.close();
+                        completion.waitForCompletion();
+                    } else {
+                        throw new IllegalArgumentException("InputStream is invalid");
+                    }
                 } finally {
                     IoUtils.closeQuietly(fos);
+                    if (tmp != null) {
+                        tmp.recycle();
+                    }
                 }
             }
         } catch (RemoteException e) {
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 046232a..2dac81c 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -129,7 +129,11 @@
      * The index of the element in the tree in prefix order. This should be used for z-layering
      * to preserve original z-layer order in the hierarchy tree assuming no "boosting" needs to
      * happen.
+     * @deprecated WindowManager may set a z-order different from the prefix order, and has set the
+     *             correct layer for the animation leash already, so this should not be used for
+     *             layer any more.
      */
+    @Deprecated
     @UnsupportedAppUsage
     public final int prefixOrderIndex;
 
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 424632fe..6541b14 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -270,8 +270,6 @@
     private Drawable mCaptionBackgroundDrawable;
     private Drawable mUserCaptionBackgroundDrawable;
 
-    private float mAvailableWidth;
-
     String mLogTag = TAG;
     private final Rect mFloatingInsets = new Rect();
     private boolean mApplyFloatingVerticalInsets = false;
@@ -315,8 +313,6 @@
         mSemiTransparentBarColor = context.getResources().getColor(
                 R.color.system_bar_background_semi_transparent, null /* theme */);
 
-        updateAvailableWidth();
-
         setWindow(window);
 
         updateLogTag(params);
@@ -697,7 +693,8 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+        final Resources res = getContext().getResources();
+        final DisplayMetrics metrics = res.getDisplayMetrics();
         final boolean isPortrait =
                 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
 
@@ -767,17 +764,19 @@
 
         if (!fixedWidth && widthMode == AT_MOST) {
             final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
+            final float availableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    res.getConfiguration().screenWidthDp, metrics);
             if (tv.type != TypedValue.TYPE_NULL) {
                 final int min;
                 if (tv.type == TypedValue.TYPE_DIMENSION) {
-                    min = (int)tv.getDimension(metrics);
+                    min = (int) tv.getDimension(metrics);
                 } else if (tv.type == TypedValue.TYPE_FRACTION) {
-                    min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
+                    min = (int) tv.getFraction(availableWidth, availableWidth);
                 } else {
                     min = 0;
                 }
                 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
-                        + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
+                        + tv.coerceToString() + ", mAvailableWidth=" + availableWidth);
 
                 if (width < min) {
                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
@@ -2144,7 +2143,6 @@
 
         updateDecorCaptionStatus(newConfig);
 
-        updateAvailableWidth();
         initializeElevation();
     }
 
@@ -2616,12 +2614,6 @@
         mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
     }
 
-    private void updateAvailableWidth() {
-        Resources res = getResources();
-        mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
-    }
-
     /**
      * @hide
      */
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index d17c328..446e580 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -384,7 +384,16 @@
         return base::unexpected(IOError::PAGES_MISSING);
       }
 
-      auto offset = dtohl(entry_offset_ptr.value());
+      uint32_t offset;
+      uint16_t res_idx;
+      if (type->flags & ResTable_type::FLAG_SPARSE) {
+        auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
+        offset = dtohs(sparse_entry->offset) * 4u;
+        res_idx  = dtohs(sparse_entry->idx);
+      } else {
+        offset = dtohl(entry_offset_ptr.value());
+        res_idx = entry_idx;
+      }
       if (offset != ResTable_type::NO_ENTRY) {
         auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
         if (!entry) {
@@ -394,7 +403,7 @@
         if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
           // The package ID will be overridden by the caller (due to runtime assignment of package
           // IDs for shared libraries).
-          return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
+          return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);
         }
       }
     }
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index f356c8130..d214e2d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -95,6 +95,38 @@
   ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
 }
 
+TEST(LoadedArscTest, FindSparseEntryApp) {
+  std::string contents;
+  ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
+                                      &contents));
+
+  std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+                                                                   contents.length());
+  ASSERT_THAT(loaded_arsc, NotNull());
+
+  const LoadedPackage* package =
+      loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_v26));
+  ASSERT_THAT(package, NotNull());
+
+  const uint8_t type_index = get_type_id(sparse::R::string::only_v26) - 1;
+  const uint16_t entry_index = get_entry_id(sparse::R::string::only_v26);
+
+  const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+  ASSERT_THAT(type_spec, NotNull());
+  ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
+
+  // Ensure that AAPT2 sparsely encoded the v26 config as expected.
+  auto type_entry = std::find_if(
+    type_spec->type_entries.begin(), type_spec->type_entries.end(),
+    [](const TypeSpec::TypeEntry& x) { return x.config.sdkVersion == 26; });
+  ASSERT_NE(type_entry, type_spec->type_entries.end());
+  ASSERT_NE(type_entry->type->flags & ResTable_type::FLAG_SPARSE, 0);
+
+  // Test fetching a resource with only sparsely encoded configs by name.
+  auto id = package->FindEntryByName(u"string", u"only_v26");
+  ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_v26, 0));
+}
+
 TEST(LoadedArscTest, LoadSharedLibrary) {
   std::string contents;
   ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h
index 243e74f..2492dbf 100644
--- a/libs/androidfw/tests/data/sparse/R.h
+++ b/libs/androidfw/tests/data/sparse/R.h
@@ -27,21 +27,22 @@
   struct integer {
     enum : uint32_t {
       foo_0 = 0x7f010000,
-      foo_1 = 0x7f010000,
-      foo_2 = 0x7f010000,
-      foo_3 = 0x7f010000,
-      foo_4 = 0x7f010000,
-      foo_5 = 0x7f010000,
-      foo_6 = 0x7f010000,
-      foo_7 = 0x7f010000,
-      foo_8 = 0x7f010000,
-      foo_9 = 0x7f010000,
+      foo_1 = 0x7f010001,
+      foo_2 = 0x7f010002,
+      foo_3 = 0x7f010003,
+      foo_4 = 0x7f010004,
+      foo_5 = 0x7f010005,
+      foo_6 = 0x7f010006,
+      foo_7 = 0x7f010007,
+      foo_8 = 0x7f010008,
+      foo_9 = 0x7f010009,
     };
   };
 
   struct string {
     enum : uint32_t {
       foo_999 = 0x7f0203e7,
+      only_v26 = 0x7f0203e8
     };
   };
 };
diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh
index e7e1d60..4ea5468 100755
--- a/libs/androidfw/tests/data/sparse/gen_strings.sh
+++ b/libs/androidfw/tests/data/sparse/gen_strings.sh
@@ -14,5 +14,7 @@
     fi
 done
 echo "</resources>" >> $OUTPUT_default
+
+echo "  <string name=\"only_v26\">only v26</string>" >> $OUTPUT_v26
 echo "</resources>" >> $OUTPUT_v26
 
diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk
index 599a370..b08a621 100644
--- a/libs/androidfw/tests/data/sparse/not_sparse.apk
+++ b/libs/androidfw/tests/data/sparse/not_sparse.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
index b6f8299..d116087e 100644
--- a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
+++ b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
@@ -333,4 +333,5 @@
   <string name="foo_993">9930</string>
   <string name="foo_996">9960</string>
   <string name="foo_999">9990</string>
+  <string name="only_v26">only v26</string>
 </resources>
diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk
index 1f9bba3..9fd01fb 100644
--- a/libs/androidfw/tests/data/sparse/sparse.apk
+++ b/libs/androidfw/tests/data/sparse/sparse.apk
Binary files differ
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
index 2272a37..2624a41 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values-v31/dimens.xml
@@ -21,10 +21,10 @@
     <dimen name="settingslib_switchbar_margin">16dp</dimen>
 
     <!-- Size of layout margin left -->
-    <dimen name="settingslib_switchbar_padding_left">24dp</dimen>
+    <dimen name="settingslib_switchbar_padding_left">20dp</dimen>
 
     <!-- Size of layout margin right -->
-    <dimen name="settingslib_switchbar_padding_right">16dp</dimen>
+    <dimen name="settingslib_switchbar_padding_right">20dp</dimen>
 
     <!-- Minimum width of switch -->
     <dimen name="settingslib_min_switch_width">52dp</dimen>
diff --git a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
index 6362882..157a54e 100644
--- a/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
+++ b/packages/SettingsLib/MainSwitchPreference/res/values/dimens.xml
@@ -24,7 +24,7 @@
     <dimen name="settingslib_restricted_icon_margin_end">16dp</dimen>
 
     <!-- Size of title margin -->
-    <dimen name="settingslib_switch_title_margin">16dp</dimen>
+    <dimen name="settingslib_switch_title_margin">24dp</dimen>
 
     <!-- SwitchBar sub settings margin start / end -->
     <dimen name="settingslib_switchbar_subsettings_margin_start">72dp</dimen>
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index bf0dc7b..1343895 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -261,8 +261,6 @@
     private void updateWifiState() {
         state = mWifiManager.getWifiState();
         enabled = state == WifiManager.WIFI_STATE_ENABLED;
-        isCarrierMerged = false;
-        subId = 0;
     }
 
     private void updateRssi(int newRssi) {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index a61b18c..a0d335d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -468,7 +468,6 @@
                 .withAlpha(1f)
                 .withMatrix(matrix)
                 .withWindowCrop(windowCrop)
-                .withLayer(window.prefixOrderIndex)
                 .withCornerRadius(cornerRadius)
                 .withVisibility(true)
                 .build()
diff --git a/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml b/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
deleted file mode 100644
index 9a69b33..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_drag_handle.xml
+++ /dev/null
@@ -1,24 +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
-  -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="36dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="?android:attr/textColorPrimary"
-        android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17" />
-</vector>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
index 363a022..eb08434 100644
--- a/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
+++ b/packages/SystemUI/res/drawable/media_output_dialog_button_background.xml
@@ -13,17 +13,20 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-       android:shape="rectangle">
-    <stroke
-        android:color="?androidprv:attr/colorAccentPrimaryVariant"
-        android:width="1dp"/>
-    <corners android:radius="20dp"/>
-    <padding
-        android:left="16dp"
-        android:right="16dp"
-        android:top="8dp"
-        android:bottom="8dp" />
-    <solid android:color="@android:color/transparent" />
-</shape>
+       android:insetBottom="6dp"
+       android:insetTop="6dp">
+    <shape android:shape="rectangle">
+        <stroke
+            android:color="?androidprv:attr/colorAccentPrimaryVariant"
+            android:width="1dp"/>
+        <corners android:radius="20dp"/>
+        <padding
+            android:left="16dp"
+            android:right="16dp"
+            android:top="8dp"
+            android:bottom="8dp"/>
+        <solid android:color="@android:color/transparent"/>
+    </shape>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index a64ef3e..07fd1b0 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -24,9 +24,9 @@
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="96dp"
+        android:layout_height="wrap_content"
         android:gravity="start|center_vertical"
-        android:paddingStart="16dp"
+        android:paddingStart="24dp"
         android:orientation="horizontal">
         <ImageView
             android:id="@+id/header_icon"
@@ -36,7 +36,7 @@
 
         <LinearLayout
             android:layout_width="match_parent"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:paddingStart="16dp"
             android:paddingTop="20dp"
             android:paddingBottom="24dp"
@@ -59,7 +59,7 @@
                 android:gravity="center_vertical"
                 android:ellipsize="end"
                 android:maxLines="1"
-                android:textColor="?android:attr/textColorTertiary"
+                android:textColor="?android:attr/textColorSecondary"
                 android:fontFamily="roboto-regular"
                 android:textSize="16sp"/>
         </LinearLayout>
@@ -90,7 +90,7 @@
         android:layout_height="wrap_content"
         android:layout_marginTop="16dp"
         android:layout_marginStart="24dp"
-        android:layout_marginBottom="18dp"
+        android:layout_marginBottom="24dp"
         android:layout_marginEnd="24dp"
         android:orientation="horizontal">
 
@@ -98,7 +98,7 @@
             android:id="@+id/stop"
             style="@style/MediaOutputRoundedOutlinedButton"
             android:layout_width="wrap_content"
-            android:layout_height="36dp"
+            android:layout_height="wrap_content"
             android:minWidth="0dp"
             android:text="@string/keyboard_key_media_stop"
             android:visibility="gone"/>
@@ -112,7 +112,7 @@
             android:id="@+id/done"
             style="@style/MediaOutputRoundedOutlinedButton"
             android:layout_width="wrap_content"
-            android:layout_height="36dp"
+            android:layout_height="wrap_content"
             android:minWidth="0dp"
             android:text="@string/inline_done_button"/>
     </LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 2ac03c2..f5c6036 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -56,17 +56,4 @@
         layout="@layout/qs_customize_panel"
         android:visibility="gone" />
 
-    <ImageView
-        android:id="@+id/qs_drag_handle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:layout_marginTop="24dp"
-        android:elevation="4dp"
-        android:importantForAccessibility="no"
-        android:scaleType="center"
-        android:src="@drawable/ic_qs_drag_handle"
-        android:tint="@color/qs_detail_button_white"
-        tools:ignore="UseAppTint" />
-
 </com.android.systemui.qs.QSContainerImpl>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 362e18d..dabc310 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -18,11 +18,14 @@
     <!-- Max number of columns for quick controls area -->
     <integer name="controls_max_columns">2</integer>
 
+    <!-- The maximum number of rows in the QSPanel -->
+    <integer name="quick_settings_max_rows">3</integer>
+
     <!-- The maximum number of rows in the QuickQSPanel -->
-    <integer name="quick_qs_panel_max_rows">4</integer>
+    <integer name="quick_qs_panel_max_rows">3</integer>
 
     <!-- The maximum number of tiles in the QuickQSPanel -->
-    <integer name="quick_qs_panel_max_tiles">8</integer>
+    <integer name="quick_qs_panel_max_tiles">6</integer>
 
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">true</bool>
diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml
index e4573c6..e0b1614 100644
--- a/packages/SystemUI/res/values-sw720dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/config.xml
@@ -18,11 +18,14 @@
     <!-- Max number of columns for quick controls area -->
     <integer name="controls_max_columns">2</integer>
 
+    <!-- The maximum number of rows in the QSPanel -->
+    <integer name="quick_settings_max_rows">3</integer>
+
     <!-- The maximum number of rows in the QuickQSPanel -->
-    <integer name="quick_qs_panel_max_rows">4</integer>
+    <integer name="quick_qs_panel_max_rows">3</integer>
 
     <!-- The maximum number of tiles in the QuickQSPanel -->
-    <integer name="quick_qs_panel_max_tiles">8</integer>
+    <integer name="quick_qs_panel_max_tiles">6</integer>
 
     <!-- Whether to use the split 2-column notification shade -->
     <bool name="config_use_split_notification_shade">true</bool>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index b6be6ed..e46b6f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.unfold.config.UnfoldTransitionConfig
 import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider
 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
 import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
 import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
@@ -62,7 +63,7 @@
         mainExecutor
     )
 
-    return if (config.isHingeAngleEnabled) {
+    val unfoldTransitionProgressProvider = if (config.isHingeAngleEnabled) {
         PhysicsBasedUnfoldTransitionProgressProvider(
             mainHandler,
             foldStateProvider
@@ -70,6 +71,10 @@
     } else {
         FixedTimingTransitionProgressProvider(foldStateProvider)
     }
+    return ScaleAwareTransitionProgressProvider(
+            unfoldTransitionProgressProvider,
+            context.contentResolver
+    )
 }
 
 fun createConfig(context: Context): UnfoldTransitionConfig =
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
index e072d41..58d7dfb 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt
@@ -2,7 +2,6 @@
 
 import android.content.Context
 import android.os.RemoteException
-import android.util.Log
 import android.view.IRotationWatcher
 import android.view.IWindowManager
 import android.view.Surface
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
new file mode 100644
index 0000000..df9078a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -0,0 +1,50 @@
+package com.android.systemui.unfold.util
+
+import android.animation.ValueAnimator
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+
+/** Wraps [UnfoldTransitionProgressProvider] to disable transitions when animations are disabled. */
+class ScaleAwareTransitionProgressProvider(
+    unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
+    private val contentResolver: ContentResolver
+) : UnfoldTransitionProgressProvider {
+
+    private val scopedUnfoldTransitionProgressProvider =
+            ScopedUnfoldTransitionProgressProvider(unfoldTransitionProgressProvider)
+
+    private val animatorDurationScaleObserver = object : ContentObserver(null) {
+        override fun onChange(selfChange: Boolean) {
+            onAnimatorScaleChanged()
+        }
+    }
+
+    init {
+        contentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
+                /* notifyForDescendants= */ false,
+                animatorDurationScaleObserver)
+        onAnimatorScaleChanged()
+    }
+
+    private fun onAnimatorScaleChanged() {
+        val animationsEnabled = ValueAnimator.areAnimatorsEnabled()
+        scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(animationsEnabled)
+    }
+
+    override fun addCallback(listener: TransitionProgressListener) {
+        scopedUnfoldTransitionProgressProvider.addCallback(listener)
+    }
+
+    override fun removeCallback(listener: TransitionProgressListener) {
+        scopedUnfoldTransitionProgressProvider.removeCallback(listener)
+    }
+
+    override fun destroy() {
+        contentResolver.unregisterContentObserver(animatorDurationScaleObserver)
+        scopedUnfoldTransitionProgressProvider.destroy()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index b2ecc614..0c1934c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -218,11 +218,14 @@
 
     @Override
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("Center in px (x, y)= (" + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
-        pw.println("Radius in pixels: " + mRadius);
-        pw.println("topLeft= (" + getX() + ", " + getY() + ")");
-        pw.println("topLeft= (" + getX() + ", " + getY() + ")");
-        pw.println("mIconType=" + typeToString(mIconType));
-        pw.println("mAod=" + mAod);
+        pw.println("Lock Icon View Parameters:");
+        pw.println("    Center in px (x, y)= ("
+                + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
+        pw.println("    Radius in pixels: " + mRadius);
+        pw.println("    mIconType=" + typeToString(mIconType));
+        pw.println("    mAod=" + mAod);
+        pw.println("Lock Icon View actual measurements:");
+        pw.println("    topLeft= (" + getX() + ", " + getY() + ")");
+        pw.println("    width=" + getWidth() + " height=" + getHeight());
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1633923..e267c5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -35,13 +35,14 @@
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.media.AudioAttributes;
 import android.os.Process;
+import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.MathUtils;
-import android.view.GestureDetector;
-import android.view.GestureDetector.SimpleOnGestureListener;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -83,6 +84,7 @@
  */
 @StatusBarComponent.StatusBarScope
 public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
+    private static final String TAG = "LockIconViewController";
     private static final float sDefaultDensity =
             (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
     private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
@@ -91,6 +93,7 @@
                 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
                 .build();
+    private static final long LONG_PRESS_TIMEOUT = 200L; // milliseconds
 
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final KeyguardViewController mKeyguardViewController;
@@ -112,6 +115,12 @@
     @Nullable private final Vibrator mVibrator;
     @Nullable private final AuthRippleController mAuthRippleController;
 
+    // Tracks the velocity of a touch to help filter out the touches that move too fast.
+    private VelocityTracker mVelocityTracker;
+    // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
+    private int mActivePointerId = -1;
+    private VibrationEffect mTick;
+
     private boolean mIsDozing;
     private boolean mIsBouncerShowing;
     private boolean mRunningFPS;
@@ -122,6 +131,7 @@
     private boolean mUserUnlockedWithBiometric;
     private Runnable mCancelDelayedUpdateVisibilityRunnable;
     private Runnable mOnGestureDetectedRunnable;
+    private Runnable mLongPressCancelRunnable;
 
     private boolean mUdfpsSupported;
     private float mHeightPixels;
@@ -181,7 +191,7 @@
         mView.setImageDrawable(mIcon);
         mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
         mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
-        dumpManager.registerDumpable("LockIconViewController", this);
+        dumpManager.registerDumpable(TAG, this);
     }
 
     @Override
@@ -320,7 +330,7 @@
                         getResources().getString(R.string.accessibility_enter_hint));
         public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
             super.onInitializeAccessibilityNodeInfo(v, info);
-            if (isClickable()) {
+            if (isActionable()) {
                 if (mShowLockIcon) {
                     info.addAction(mAccessibilityAuthenticateHint);
                 } else if (mShowUnlockIcon) {
@@ -425,10 +435,8 @@
             mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
         }
 
-        if (mShowAodLockIcon) {
-            mView.setTranslationX(offsetX);
-            mView.setTranslationY(offsetY);
-        }
+        mView.setTranslationX(offsetX);
+        mView.setTranslationY(offsetY);
     }
 
     private void updateIsUdfpsEnrolled() {
@@ -477,7 +485,7 @@
                 @Override
                 public void onKeyguardVisibilityChanged(boolean showing) {
                     // reset mIsBouncerShowing state in case it was preemptively set
-                    // onAffordanceClick
+                    // onLongPress
                     mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
                     updateVisibility();
                 }
@@ -571,104 +579,79 @@
         }
     };
 
-    private final GestureDetector mGestureDetector =
-            new GestureDetector(new SimpleOnGestureListener() {
-                public boolean onDown(MotionEvent e) {
-                    if (!isClickable()) {
-                        mDownDetected = false;
-                        return false;
-                    }
-
-                    // intercept all following touches until we see MotionEvent.ACTION_CANCEL UP or
-                    // MotionEvent.ACTION_UP (see #onTouchEvent)
-                    if (mVibrator != null && !mDownDetected) {
-                        mVibrator.vibrate(
-                                Process.myUid(),
-                                getContext().getOpPackageName(),
-                                UdfpsController.EFFECT_CLICK,
-                                "lockIcon-onDown",
-                                VIBRATION_SONIFICATION_ATTRIBUTES);
-                    }
-
-                    mDownDetected = true;
-                    return true;
-                }
-
-                public void onLongPress(MotionEvent e) {
-                    if (!wasClickableOnDownEvent()) {
-                        return;
-                    }
-
-                    if (onAffordanceClick() && mVibrator != null) {
-                        // only vibrate if the click went through and wasn't intercepted by falsing
-                        mVibrator.vibrate(
-                                Process.myUid(),
-                                getContext().getOpPackageName(),
-                                UdfpsController.EFFECT_CLICK,
-                                "lockIcon-onLongPress",
-                                VIBRATION_SONIFICATION_ATTRIBUTES);
-                    }
-                }
-
-                public boolean onSingleTapUp(MotionEvent e) {
-                    if (!wasClickableOnDownEvent()) {
-                        return false;
-                    }
-                    onAffordanceClick();
-                    return true;
-                }
-
-                public boolean onFling(MotionEvent e1, MotionEvent e2,
-                        float velocityX, float velocityY) {
-                    if (!wasClickableOnDownEvent()) {
-                        return false;
-                    }
-                    onAffordanceClick();
-                    return true;
-                }
-
-                private boolean wasClickableOnDownEvent() {
-                    return mDownDetected;
-                }
-
-                /**
-                 * Whether we tried to launch the affordance.
-                 *
-                 * If falsing intercepts the click, returns false.
-                 */
-                private boolean onAffordanceClick() {
-                    if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
-                        return false;
-                    }
-
-                    // pre-emptively set to true to hide view
-                    mIsBouncerShowing = true;
-                    if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
-                        mAuthRippleController.showRipple(FINGERPRINT);
-                    }
-                    updateVisibility();
-                    if (mOnGestureDetectedRunnable != null) {
-                        mOnGestureDetectedRunnable.run();
-                    }
-                    mKeyguardViewController.showBouncer(/* scrim */ true);
-                    return true;
-                }
-            });
-
     /**
-     * Send touch events to this view and handles it if the touch is within this view and we are
-     * in a 'clickable' state
-     * @return whether to intercept the touch event
+     * Handles the touch if it is within the lock icon view and {@link #isActionable()} is true.
+     * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
+     * area for {@link #LONG_PRESS_TIMEOUT} ms.
+     *
+     * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
      */
     public boolean onTouchEvent(MotionEvent event, Runnable onGestureDetectedRunnable) {
-        if (onInterceptTouchEvent(event)) {
-            mOnGestureDetectedRunnable = onGestureDetectedRunnable;
-            mGestureDetector.onTouchEvent(event);
-            return true;
+        if (!onInterceptTouchEvent(event)) {
+            cancelTouches();
+            return false;
         }
 
-        mDownDetected = false;
-        return false;
+        mOnGestureDetectedRunnable = onGestureDetectedRunnable;
+        switch(event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_HOVER_ENTER:
+                if (mVibrator != null && !mDownDetected) {
+                    if (mTick == null) {
+                        mTick = UdfpsController.lowTick(getContext(), true,
+                                LONG_PRESS_TIMEOUT);
+                    }
+                    mVibrator.vibrate(
+                            Process.myUid(),
+                            getContext().getOpPackageName(),
+                            mTick,
+                            "lock-icon-tick",
+                            VIBRATION_SONIFICATION_ATTRIBUTES);
+                }
+
+                // The pointer that causes ACTION_DOWN is always at index 0.
+                // We need to persist its ID to track it during ACTION_MOVE that could include
+                // data for many other pointers because of multi-touch support.
+                mActivePointerId = event.getPointerId(0);
+                if (mVelocityTracker == null) {
+                    // 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.
+                    mVelocityTracker = VelocityTracker.obtain();
+                } else {
+                    // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
+                    // ACTION_DOWN, in that case we should just reuse the old instance.
+                    mVelocityTracker.clear();
+                }
+                mVelocityTracker.addMovement(event);
+
+                mDownDetected = true;
+                mLongPressCancelRunnable = mExecutor.executeDelayed(
+                        this::onLongPress, LONG_PRESS_TIMEOUT);
+                break;
+            case MotionEvent.ACTION_MOVE:
+            case MotionEvent.ACTION_HOVER_MOVE:
+                mVelocityTracker.addMovement(event);
+                // Compute pointer velocity in pixels per second.
+                mVelocityTracker.computeCurrentVelocity(1000);
+                float velocity = UdfpsController.computePointerSpeed(mVelocityTracker,
+                        mActivePointerId);
+                if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
+                        && UdfpsController.exceedsVelocityThreshold(velocity)) {
+                    Log.v(TAG, "lock icon long-press rescheduled due to "
+                            + "high pointer velocity=" + velocity);
+                    mLongPressCancelRunnable.run();
+                    mLongPressCancelRunnable = mExecutor.executeDelayed(
+                            this::onLongPress, LONG_PRESS_TIMEOUT);
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_HOVER_EXIT:
+                cancelTouches();
+                break;
+        }
+
+        return true;
     }
 
     /**
@@ -676,7 +659,7 @@
      * bounds.
      */
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        if (!inLockIconArea(event) || !isClickable()) {
+        if (!inLockIconArea(event) || !isActionable()) {
             return false;
         }
 
@@ -687,13 +670,59 @@
         return mDownDetected;
     }
 
+    private void onLongPress() {
+        cancelTouches();
+        if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+            Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
+            return;
+        }
+
+        // pre-emptively set to true to hide view
+        mIsBouncerShowing = true;
+        if (mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
+            mAuthRippleController.showRipple(FINGERPRINT);
+        }
+        updateVisibility();
+        if (mOnGestureDetectedRunnable != null) {
+            mOnGestureDetectedRunnable.run();
+        }
+
+        if (mVibrator != null) {
+            // play device entry haptic (same as biometric success haptic)
+            mVibrator.vibrate(
+                    Process.myUid(),
+                    getContext().getOpPackageName(),
+                    UdfpsController.EFFECT_CLICK,
+                    "lock-icon-device-entry",
+                    VIBRATION_SONIFICATION_ATTRIBUTES);
+        }
+
+        mKeyguardViewController.showBouncer(/* scrim */ true);
+    }
+
+
+    private void cancelTouches() {
+        mDownDetected = false;
+        if (mLongPressCancelRunnable != null) {
+            mLongPressCancelRunnable.run();
+        }
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+        if (mVibrator != null) {
+            mVibrator.cancel();
+        }
+    }
+
+
     private boolean inLockIconArea(MotionEvent event) {
         return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
                 && (mView.getVisibility() == View.VISIBLE
                 || (mAodFp != null && mAodFp.getVisibility() == View.VISIBLE));
     }
 
-    private boolean isClickable() {
+    private boolean isActionable() {
         return mUdfpsSupported || mShowUnlockIcon;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 09f2af4..9808045 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -103,6 +103,7 @@
 public class UdfpsController implements DozeReceiver {
     private static final String TAG = "UdfpsController";
     private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
+    private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds
 
     // Minimum required delay between consecutive touch logs in milliseconds.
     private static final long MIN_TOUCH_LOG_INTERVAL = 50;
@@ -164,8 +165,7 @@
     private boolean mAttemptedToDismissKeyguard;
     private Set<Callback> mCallbacks = new HashSet<>();
 
-    // by default, use low tick
-    private int mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
+    private static final int DEFAULT_TICK = VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
     private final VibrationEffect mTick;
 
     @VisibleForTesting
@@ -327,12 +327,23 @@
         }
     }
 
-    private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
+    /**
+     * Calculate the pointer speed given a velocity tracker and the pointer id.
+     * This assumes that the velocity tracker has already been passed all relevant motion events.
+     */
+    public static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
         final float vx = tracker.getXVelocity(pointerId);
         final float vy = tracker.getYVelocity(pointerId);
         return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
     }
 
+    /**
+     * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
+     */
+    public static boolean exceedsVelocityThreshold(float velocity) {
+        return velocity > 750f;
+    }
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -467,7 +478,7 @@
                         final float v = computePointerSpeed(mVelocityTracker, mActivePointerId);
                         final float minor = event.getTouchMinor(idx);
                         final float major = event.getTouchMajor(idx);
-                        final boolean exceedsVelocityThreshold = v > 750f;
+                        final boolean exceedsVelocityThreshold = exceedsVelocityThreshold(v);
                         final String touchInfo = String.format(
                                 "minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
                                 minor, major, v, exceedsVelocityThreshold);
@@ -575,7 +586,7 @@
         mConfigurationController = configurationController;
         mSystemClock = systemClock;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
-        mTick = lowTick();
+        mTick = lowTick(context, false /* useShortRampup */, DEFAULT_VIBRATION_DURATION);
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -610,32 +621,43 @@
         udfpsHapticsSimulator.setUdfpsController(this);
     }
 
-    private VibrationEffect lowTick() {
-        boolean useLowTickDefault = mContext.getResources()
+    /**
+     * Returns the continuous low tick effect that starts playing on the udfps finger-down event.
+     */
+    public static VibrationEffect lowTick(
+            Context context,
+            boolean useShortRampUp,
+            long duration
+    ) {
+        boolean useLowTickDefault = context.getResources()
                 .getBoolean(R.bool.config_udfpsUseLowTick);
+        int primitiveTick = DEFAULT_TICK;
         if (Settings.Global.getFloat(
-                mContext.getContentResolver(),
+                context.getContentResolver(),
                 "tick-low", useLowTickDefault ? 1 : 0) == 0) {
-            mPrimitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
+            primitiveTick = VibrationEffect.Composition.PRIMITIVE_TICK;
         }
         float tickIntensity = Settings.Global.getFloat(
-                mContext.getContentResolver(),
+                context.getContentResolver(),
                 "tick-intensity",
-                mContext.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
+                context.getResources().getFloat(R.dimen.config_udfpsTickIntensity));
         int tickDelay = Settings.Global.getInt(
-                mContext.getContentResolver(),
+                context.getContentResolver(),
                 "tick-delay",
-                mContext.getResources().getInteger(R.integer.config_udfpsTickDelay));
+                context.getResources().getInteger(R.integer.config_udfpsTickDelay));
 
         VibrationEffect.Composition composition = VibrationEffect.startComposition();
-        composition.addPrimitive(mPrimitiveTick, tickIntensity, 0);
-        int primitives = 1000 / tickDelay;
+        composition.addPrimitive(primitiveTick, tickIntensity, 0);
+        int primitives = (int) (duration / tickDelay);
         float[] rampUp = new float[]{.48f, .58f, .69f, .83f};
+        if (useShortRampUp) {
+            rampUp = new float[]{.5f, .7f};
+        }
         for (int i = 0; i < rampUp.length; i++) {
-            composition.addPrimitive(mPrimitiveTick, tickIntensity * rampUp[i], tickDelay);
+            composition.addPrimitive(primitiveTick, tickIntensity * rampUp[i], tickDelay);
         }
         for (int i = rampUp.length; i < primitives; i++) {
-            composition.addPrimitive(mPrimitiveTick, tickIntensity, tickDelay);
+            composition.addPrimitive(primitiveTick, tickIntensity, tickDelay);
         }
         return composition.compose();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index 71edbc0..d17eadd 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -19,6 +19,7 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
 import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
+import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
 
 import android.provider.DeviceConfig;
@@ -71,7 +72,9 @@
             return Result.passed(0);
         }
 
-        if (interactionType == LEFT_AFFORDANCE || interactionType == RIGHT_AFFORDANCE) {
+        if (interactionType == LEFT_AFFORDANCE
+                || interactionType == RIGHT_AFFORDANCE
+                || interactionType == LOCK_ICON) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 03a0977..c12d48d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -63,13 +63,14 @@
     View mDialogView;
     private TextView mHeaderTitle;
     private TextView mHeaderSubtitle;
-    private ImageView mHeaderIcon;
     private RecyclerView mDevicesRecyclerView;
     private LinearLayout mDeviceListLayout;
     private Button mDoneButton;
     private Button mStopButton;
     private int mListMaxHeight;
 
+    protected ImageView mHeaderIcon;
+
     MediaOutputBaseAdapter mAdapter;
 
     private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
@@ -140,7 +141,6 @@
         mMediaOutputController.stop();
     }
 
-    @VisibleForTesting
     void refresh() {
         // Update header icon
         final int iconRes = getHeaderIconRes();
@@ -154,12 +154,6 @@
         } else {
             mHeaderIcon.setVisibility(View.GONE);
         }
-        if (mHeaderIcon.getVisibility() == View.VISIBLE) {
-            final int size = getHeaderIconSize();
-            final int padding = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.media_output_dialog_header_icon_padding);
-            mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
-        }
         // Update title and subtitle
         mHeaderTitle.setText(getHeaderText());
         final CharSequence subTitle = getHeaderSubtitle();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
index 1300400..b41e813 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java
@@ -20,6 +20,7 @@
 import android.os.Bundle;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.LinearLayout;
 
 import androidx.core.graphics.drawable.IconCompat;
 
@@ -76,6 +77,15 @@
     }
 
     @Override
+    void refresh() {
+        super.refresh();
+        final int size = getHeaderIconSize();
+        final int padding = mContext.getResources().getDimensionPixelSize(
+                R.dimen.media_output_dialog_header_icon_padding);
+        mHeaderIcon.setLayoutParams(new LinearLayout.LayoutParams(size + padding, size));
+    }
+
+    @Override
     int getStopButtonVisibility() {
         return View.VISIBLE;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index cdf770f..c3de3c5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -110,6 +110,16 @@
     }
 
     @Override
+    public int getTilesHeight() {
+        // Use the first page as that is the maximum height we need to show.
+        TileLayout tileLayout = mPages.get(0);
+        if (tileLayout == null) {
+            return 0;
+        }
+        return tileLayout.getTilesHeight();
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         // Pass configuration change to non-attached pages as well. Some config changes will cause
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 8588ddf..e230e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -27,7 +27,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
@@ -53,7 +52,6 @@
     private float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private NonInterceptingScrollView mQSPanelContainer;
-    private ImageView mDragHandle;
 
     private int mSideMargins;
     private boolean mQsDisabled;
@@ -71,7 +69,6 @@
         mQSDetail = findViewById(R.id.qs_detail);
         mHeader = findViewById(R.id.header);
         mQSCustomizer = findViewById(R.id.qs_customize);
-        mDragHandle = findViewById(R.id.qs_drag_handle);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
     }
 
@@ -190,23 +187,14 @@
         mQSDetail.setBottom(getTop() + scrollBottom);
         int qsDetailBottomMargin = ((MarginLayoutParams) mQSDetail.getLayoutParams()).bottomMargin;
         mQSDetail.setBottom(getTop() + scrollBottom - qsDetailBottomMargin);
-        // Pin the drag handle to the bottom of the panel.
-        mDragHandle.setTranslationY(scrollBottom - mDragHandle.getHeight());
     }
 
     protected int calculateContainerHeight() {
         int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
         // Need to add the dragHandle height so touches will be intercepted by it.
-        int dragHandleHeight;
-        if (mDragHandle.getVisibility() == VISIBLE) {
-            dragHandleHeight = Math.round((1 - mQsExpansion) * mDragHandle.getHeight());
-        } else {
-            dragHandleHeight = 0;
-        }
         return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
                 : Math.round(mQsExpansion * (heightOverride - mHeader.getHeight()))
-                + mHeader.getHeight()
-                + dragHandleHeight;
+                + mHeader.getHeight();
     }
 
     int calculateContainerBottom() {
@@ -221,8 +209,6 @@
     public void setExpansion(float expansion) {
         mQsExpansion = expansion;
         mQSPanelContainer.setScrollingEnabled(expansion > 0f);
-        mDragHandle.setAlpha(1.0f - expansion);
-        mDragHandle.setClickable(expansion == 0f); // Only clickable when fully collapsed
         updateExpansion();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index ee59ae6..e82e9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -33,7 +33,6 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout.LayoutParams;
-import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
@@ -94,7 +93,6 @@
     private float mLastPanelFraction;
     private float mSquishinessFraction = 1;
     private boolean mQsDisabled;
-    private ImageView mQsDragHandler;
 
     private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
     private final CommandQueue mCommandQueue;
@@ -205,7 +203,6 @@
         mHeader = view.findViewById(R.id.header);
         mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container));
         mFooter = qsFragmentComponent.getQSFooter();
-        mQsDragHandler = view.findViewById(R.id.qs_drag_handle);
 
         mQsDetailDisplayer.setQsPanelController(mQSPanelController);
 
@@ -249,11 +246,6 @@
                     mQSPanelController.getMediaHost().getHostView().setAlpha(1.0f);
                     mQSAnimator.requestAnimatorUpdate();
                 });
-
-        mQsDragHandler.setOnClickListener(v -> {
-            Log.d(TAG, "drag handler clicked");
-            mCommandQueue.animateExpandSettingsPanel(null);
-        });
     }
 
     @Override
@@ -385,30 +377,26 @@
     }
 
     private void updateQsState() {
-        final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling
+        final boolean expanded = mQsExpanded || mInSplitShade;
+        final boolean expandVisually = expanded || mStackScrollerOverscrolling
                 || mHeaderAnimating;
-        mQSPanelController.setExpanded(mQsExpanded);
-        mQSDetail.setExpanded(mQsExpanded);
+        mQSPanelController.setExpanded(expanded);
+        mQSDetail.setExpanded(expanded);
         boolean keyguardShowing = isKeyguardState();
-        mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
+        mHeader.setVisibility((expanded || !keyguardShowing || mHeaderAnimating
                 || mShowCollapsedOnKeyguard)
                 ? View.VISIBLE
                 : View.INVISIBLE);
         mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
-                || (mQsExpanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
-        mFooter.setVisibility(!mQsDisabled && (mQsExpanded || !keyguardShowing || mHeaderAnimating
+                || (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
+        mFooter.setVisibility(!mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
                 || mShowCollapsedOnKeyguard)
                 ? View.VISIBLE
                 : View.INVISIBLE);
         mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
-                || (mQsExpanded && !mStackScrollerOverscrolling));
+                || (expanded && !mStackScrollerOverscrolling));
         mQSPanelController.setVisibility(
                 !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE);
-        mQsDragHandler.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating
-                || mShowCollapsedOnKeyguard)
-                && Utils.shouldUseSplitNotificationShade(getResources())
-                ? View.VISIBLE
-                : View.GONE);
     }
 
     private boolean isKeyguardState() {
@@ -418,7 +406,8 @@
     }
 
     private void updateShowCollapsedOnKeyguard() {
-        boolean showCollapsed = mBypassController.getBypassEnabled() || mTransitioningToFullShade;
+        boolean showCollapsed = mBypassController.getBypassEnabled()
+                || (mTransitioningToFullShade && !mInSplitShade);
         if (showCollapsed != mShowCollapsedOnKeyguard) {
             mShowCollapsedOnKeyguard = showCollapsed;
             updateQsState();
@@ -498,6 +487,8 @@
     public void setInSplitShade(boolean inSplitShade) {
         mInSplitShade = inSplitShade;
         mQSAnimator.setTranslateWhileExpanding(inSplitShade);
+        updateShowCollapsedOnKeyguard();
+        updateQsState();
     }
 
     @Override
@@ -516,7 +507,8 @@
     public void setQsExpansion(float expansion, float panelExpansionFraction,
             float proposedTranslation, float squishinessFraction) {
         float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
-        float progress = mTransitioningToFullShade ? mFullShadeProgress : panelExpansionFraction;
+        float progress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
+                ? mFullShadeProgress : panelExpansionFraction;
         setAlphaAnimationProgress(mInSplitShade ? progress : 1);
         mContainer.setExpansion(expansion);
         final float translationScaleY = (mInSplitShade
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index d69deef..20c0fdd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -258,13 +258,8 @@
     }
 
     private void updateViewPositions() {
-        if (!(mTileLayout instanceof TileLayout)) {
-            return;
-        }
-        TileLayout layout = (TileLayout) mTileLayout;
-
         // Adjust view positions based on tile squishing
-        int tileHeightOffset = layout.getTilesHeight() - layout.getHeight();
+        int tileHeightOffset = mTileLayout.getTilesHeight() - mTileLayout.getHeight();
 
         boolean move = false;
         for (int i = 0; i < getChildCount(); i++) {
@@ -787,6 +782,12 @@
         /** */
         void setListening(boolean listening, UiEventLogger uiEventLogger);
 
+        /** */
+        int getHeight();
+
+        /** */
+        int getTilesHeight();
+
         /**
          * Sets a size modifier for the tile. Where 0 means collapsed, and 1 expanded.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 6794d5b..001c740e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -45,6 +45,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.Utils;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -72,6 +73,7 @@
             new QSPanel.OnConfigurationChangedListener() {
         @Override
         public void onConfigurationChange(Configuration newConfig) {
+            updateMediaExpansion();
             mView.updateResources();
             mQsSecurityFooter.onConfigurationChanged();
             if (mView.isListening()) {
@@ -121,13 +123,17 @@
     @Override
     public void onInit() {
         super.onInit();
-        mMediaHost.setExpansion(1);
+        updateMediaExpansion();
         mMediaHost.setShowsOnlyActiveMedia(false);
         mMediaHost.init(MediaHierarchyManager.LOCATION_QS);
         mQsCustomizerController.init();
         mBrightnessSliderController.init();
     }
 
+    private void updateMediaExpansion() {
+        mMediaHost.setExpansion(Utils.shouldUseSplitNotificationShade(getResources()) ? 0 : 1);
+    }
+
     @Override
     protected void onViewAttached() {
         super.onViewAttached();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 7f08e5b..bff318a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -275,6 +275,7 @@
         return Math.max(mColumns * mRows, 1);
     }
 
+    @Override
     public int getTilesHeight() {
         return mLastTileBottom + getPaddingBottom();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 706f423..6f63a08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -86,6 +86,7 @@
 import com.android.wifitrackerlib.MergedCarrierEntry;
 import com.android.wifitrackerlib.WifiEntry;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -98,8 +99,10 @@
 
 import javax.inject.Inject;
 
-public class InternetDialogController implements WifiEntry.DisconnectCallback,
-        AccessPointController.AccessPointCallback {
+/**
+ * Controller for Internet Dialog.
+ */
+public class InternetDialogController implements AccessPointController.AccessPointCallback {
 
     private static final String TAG = "InternetDialogController";
     private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
@@ -881,20 +884,6 @@
             return;
         }
 
-        boolean hasConnectedWifi = false;
-        final int accessPointSize = accessPoints.size();
-        for (int i = 0; i < accessPointSize; i++) {
-            WifiEntry wifiEntry = accessPoints.get(i);
-            if (wifiEntry.isDefaultNetwork() && wifiEntry.hasInternetAccess()) {
-                mConnectedEntry = wifiEntry;
-                hasConnectedWifi = true;
-                break;
-            }
-        }
-        if (!hasConnectedWifi) {
-            mConnectedEntry = null;
-        }
-
         int count = MAX_WIFI_ENTRY_COUNT;
         if (mHasEthernet) {
             count -= 1;
@@ -902,15 +891,22 @@
         if (hasActiveSubId()) {
             count -= 1;
         }
-        if (hasConnectedWifi) {
-            count -= 1;
+        if (count > accessPoints.size()) {
+            count = accessPoints.size();
         }
-        final List<WifiEntry> wifiEntries = accessPoints.stream()
-                .filter(wifiEntry -> (!wifiEntry.isDefaultNetwork()
-                        || !wifiEntry.hasInternetAccess()))
-                .limit(count)
-                .collect(Collectors.toList());
-        mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
+
+        WifiEntry connectedEntry = null;
+        final List<WifiEntry> wifiEntries = new ArrayList<>();
+        for (int i = 0; i < count; i++) {
+            WifiEntry entry = accessPoints.get(i);
+            if (connectedEntry == null && entry.isDefaultNetwork() && entry.hasInternetAccess()) {
+                connectedEntry = entry;
+            } else {
+                wifiEntries.add(entry);
+            }
+        }
+        mConnectedEntry = connectedEntry;
+        mWifiEntriesCount = wifiEntries.size();
 
         if (mCallback != null) {
             mCallback.onAccessPointsChanged(wifiEntries, mConnectedEntry);
@@ -921,10 +917,6 @@
     public void onSettingsActivityTriggered(Intent settingsIntent) {
     }
 
-    @Override
-    public void onDisconnectResult(int status) {
-    }
-
     private class InternetTelephonyCallback extends TelephonyCallback implements
             TelephonyCallback.DataConnectionStateListener,
             TelephonyCallback.DisplayInfoListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index dca7f70..0fb08e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -174,7 +174,7 @@
     internal fun canDragDown(): Boolean {
         return (statusBarStateController.state == StatusBarState.KEYGUARD ||
                 nsslController.isInLockedDownShade()) &&
-                qS.isFullyCollapsed
+                (qS.isFullyCollapsed || useSplitShade)
     }
 
     /**
@@ -285,7 +285,7 @@
     internal val isDragDownAnywhereEnabled: Boolean
         get() = (statusBarStateController.getState() == StatusBarState.KEYGUARD &&
                 !keyguardBypassController.bypassEnabled &&
-                qS.isFullyCollapsed)
+                (qS.isFullyCollapsed || useSplitShade))
 
     /**
      * The amount in pixels that the user has dragged down.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 103ca0e..ff9d919 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -83,7 +83,7 @@
     @Override
     public void notifyListeners(SignalCallback callback) {
         if (mCurrentState.isCarrierMerged) {
-            if (mCurrentState.isDefault) {
+            if (mCurrentState.isDefault || !mNetworkController.isRadioOn()) {
                 notifyListenersForCarrierWifi(callback);
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 4441270..fa9217d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -20,6 +20,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
@@ -158,6 +159,9 @@
 
             mExpansionStateLogger.onVisibilityChanged(
                     mTmpCurrentlyVisibleNotifications, mTmpCurrentlyVisibleNotifications);
+            Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Active]", N);
+            Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Visible]",
+                    mCurrentlyVisibleNotifications.size());
 
             recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
             mTmpCurrentlyVisibleNotifications.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index ab08865..5477c19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -119,6 +119,7 @@
 import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
@@ -726,38 +727,59 @@
         }
 
         if (DEBUG) {
-            int y = mTopPadding;
-            mDebugPaint.setColor(Color.RED);
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
-            y = getLayoutHeight();
-            mDebugPaint.setColor(Color.YELLOW);
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
-            y = (int) mMaxLayoutHeight;
-            mDebugPaint.setColor(Color.MAGENTA);
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
-            if (mKeyguardBottomPadding >= 0) {
-                y = getHeight() - (int) mKeyguardBottomPadding;
-                mDebugPaint.setColor(Color.GRAY);
-                canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-            }
-
-            y = getHeight() - getEmptyBottomMargin();
-            mDebugPaint.setColor(Color.GREEN);
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
-            y = (int) (mAmbientState.getStackY());
-            mDebugPaint.setColor(Color.CYAN);
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-
-            y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
-            mDebugPaint.setColor(Color.BLUE);
-            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            onDrawDebug(canvas);
         }
     }
 
+    /** Used to track the Y positions that were already used to draw debug text labels. */
+    private static final Set<Integer> DEBUG_TEXT_USED_Y_POSITIONS =
+            DEBUG ? new HashSet<>() : Collections.emptySet();
+
+    private void onDrawDebug(Canvas canvas) {
+        DEBUG_TEXT_USED_Y_POSITIONS.clear();
+
+        int y = mTopPadding;
+        drawDebugInfo(canvas, y, Color.RED, /* label= */ "mTopPadding");
+
+        y = getLayoutHeight();
+        drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "getLayoutHeight()");
+
+        y = (int) mMaxLayoutHeight;
+        drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight");
+
+        if (mKeyguardBottomPadding >= 0) {
+            y = getHeight() - (int) mKeyguardBottomPadding;
+            drawDebugInfo(canvas, y, Color.GRAY,
+                    /* label= */ "getHeight() - mKeyguardBottomPadding");
+        }
+
+        y = getHeight() - getEmptyBottomMargin();
+        drawDebugInfo(canvas, y, Color.GREEN, /* label= */ "getHeight() - getEmptyBottomMargin()");
+
+        y = (int) (mAmbientState.getStackY());
+        drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY()");
+
+        y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight());
+        drawDebugInfo(canvas, y, Color.BLUE,
+                /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight()");
+    }
+
+    private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
+        mDebugPaint.setColor(color);
+        canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ getWidth(), /* stopY= */ y,
+                mDebugPaint);
+        canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+    }
+
+    private int computeDebugYTextPosition(int lineY) {
+        int textY = lineY;
+        while (DEBUG_TEXT_USED_Y_POSITIONS.contains(textY)) {
+            textY = (int) (textY + mDebugPaint.getTextSize());
+        }
+        DEBUG_TEXT_USED_Y_POSITIONS.add(textY);
+        return textY;
+    }
+
     @ShadeViewRefactor(RefactorComponent.DECORATOR)
     private void drawBackground(Canvas canvas) {
         int lockScreenLeft = mSidePaddings;
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 c8bca7c..261b5db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2248,11 +2248,14 @@
 
     private void updateQsExpansion() {
         if (mQs == null) return;
-        float qsExpansionFraction = computeQsExpansionFraction();
-        float squishiness = mNotificationStackScrollLayoutController
-                .getNotificationSquishinessFraction();
-        mQs.setQsExpansion(qsExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
-                mQsExpandImmediate || mQsExpanded ? 1f : squishiness);
+        final float squishiness =
+                mQsExpandImmediate || mQsExpanded ? 1f : mNotificationStackScrollLayoutController
+                        .getNotificationSquishinessFraction();
+        final float qsExpansionFraction = computeQsExpansionFraction();
+        final float adjustedExpansionFraction = mShouldUseSplitNotificationShade
+                ? 1f : computeQsExpansionFraction();
+        mQs.setQsExpansion(adjustedExpansionFraction, getExpandedFraction(), getHeaderTranslation(),
+                squishiness);
         mSplitShadeHeaderController.setQsExpandedFraction(qsExpansionFraction);
         mMediaHierarchyManager.setQsExpansion(qsExpansionFraction);
         int qsPanelBottomY = calculateQsBottomPosition(qsExpansionFraction);
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 51de1321..e6fc49f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -15,6 +15,7 @@
  */
 package com.android.systemui.unfold
 
+import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.PixelFormat
 import android.hardware.devicestate.DeviceStateManager
@@ -111,7 +112,7 @@
         Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
         try {
             // Add the view only if we are unfolding and this is the first screen on
-            if (!isFolded && !isUnfoldHandled) {
+            if (!isFolded && !isUnfoldHandled && ValueAnimator.areAnimatorsEnabled()) {
                 addView(onOverlayReady)
                 isUnfoldHandled = true
             } else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
index e01583e..81bcbfb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java
@@ -16,12 +16,14 @@
 
 package com.android.systemui.keyguard;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
 
 import static junit.framework.Assert.assertEquals;
 
 import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.never;
@@ -57,6 +59,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.biometrics.AuthController;
 import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.doze.util.BurnInHelperKt;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -68,6 +71,7 @@
 
 import com.airbnb.lottie.LottieAnimationView;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -76,6 +80,8 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -86,6 +92,8 @@
 public class LockIconViewControllerTest extends SysuiTestCase {
     private static final String UNLOCKED_LABEL = "unlocked";
 
+    private MockitoSession mStaticMockSession;
+
     private @Mock LockIconView mLockIconView;
     private @Mock AnimatedStateListDrawable mIconDrawable;
     private @Mock Context mContext;
@@ -133,6 +141,10 @@
 
     @Before
     public void setUp() throws Exception {
+        mStaticMockSession = mockitoSession()
+                .mockStatic(BurnInHelperKt.class)
+                .strictness(Strictness.LENIENT)
+                .startMocking();
         MockitoAnnotations.initMocks(this);
 
         when(mLockIconView.getResources()).thenReturn(mResources);
@@ -169,6 +181,11 @@
         );
     }
 
+    @After
+    public void tearDown() {
+        mStaticMockSession.finishMocking();
+    }
+
     @Test
     public void testIgnoreUdfpsWhenNotSupported() {
         // GIVEN Udpfs sensor is NOT available
@@ -369,6 +386,42 @@
         verify(mLockIconView).updateIcon(ICON_LOCK, true);
     }
 
+    @Test
+    public void testBurnInOffsetsUpdated_onDozeAmountChanged() {
+        // GIVEN udfps enrolled
+        setupUdfps();
+        when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
+
+        // GIVEN burn-in offset = 5
+        int burnInOffset = 5;
+        when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset);
+
+        // GIVEN starting state for the lock icon (keyguard)
+        setupShowLockIcon();
+        mLockIconViewController.init();
+        captureAttachListener();
+        mAttachListener.onViewAttachedToWindow(mLockIconView);
+        captureStatusBarStateListener();
+        reset(mLockIconView);
+
+        // WHEN dozing updates
+        mStatusBarStateListener.onDozingChanged(true /* isDozing */);
+        mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
+
+        // THEN the view's translation is updated to use the AoD burn-in offsets
+        verify(mLockIconView).setTranslationY(burnInOffset);
+        verify(mLockIconView).setTranslationX(burnInOffset);
+        reset(mLockIconView);
+
+        // WHEN the device is no longer dozing
+        mStatusBarStateListener.onDozingChanged(false /* isDozing */);
+        mStatusBarStateListener.onDozeAmountChanged(0f, 0f);
+
+        // THEN the view is updated to NO translation (no burn-in offsets anymore)
+        verify(mLockIconView).setTranslationY(0);
+        verify(mLockIconView).setTranslationX(0);
+
+    }
     private Pair<Integer, PointF> setupUdfps() {
         when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
         final PointF udfpsLocation = new PointF(50, 75);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index ca8903b..95e7a98c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -1,5 +1,7 @@
 package com.android.systemui.qs.tiles.dialog;
 
+import static android.provider.Settings.Global.AIRPLANE_MODE_ON;
+
 import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
 import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
 
@@ -19,7 +21,6 @@
 import static org.mockito.Mockito.when;
 
 import android.animation.Animator;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
@@ -37,7 +38,6 @@
 import android.view.View;
 import android.view.WindowManager;
 
-import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -47,7 +47,6 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogLaunchAnimator;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.connectivity.AccessPointController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -70,7 +69,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.Executor;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -78,8 +76,6 @@
 public class InternetDialogControllerTest extends SysuiTestCase {
 
     private static final int SUB_ID = 1;
-    private static final String CONNECTED_TITLE = "Connected Wi-Fi Title";
-    private static final String CONNECTED_SUMMARY = "Connected Wi-Fi Summary";
 
     //SystemUIToast
     private static final int GRAVITY_FLAGS = Gravity.FILL_HORIZONTAL | Gravity.FILL_VERTICAL;
@@ -142,7 +138,7 @@
     private DialogLaunchAnimator mDialogLaunchAnimator;
 
     private TestableResources mTestableResources;
-    private MockInternetDialogController mInternetDialogController;
+    private InternetDialogController mInternetDialogController;
     private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
     private List<WifiEntry> mAccessPoints = new ArrayList<>();
     private List<WifiEntry> mWifiEntries = new ArrayList<>();
@@ -170,7 +166,7 @@
         when(mSystemUIToast.getGravity()).thenReturn(GRAVITY_FLAGS);
         when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator);
 
-        mInternetDialogController = new MockInternetDialogController(mContext,
+        mInternetDialogController = new InternetDialogController(mContext,
                 mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
                 mSubscriptionManager, mTelephonyManager, mWifiManager,
                 mock(ConnectivityManager.class), mHandler, mExecutor, mBroadcastDispatcher,
@@ -225,7 +221,7 @@
 
     @Test
     public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
 
         assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
                 getResourcesString("airplane_mode")));
@@ -233,7 +229,7 @@
 
     @Test
     public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
 
         assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
                 getResourcesString("quick_settings_internet_label")));
@@ -241,14 +237,14 @@
 
     @Test
     public void getSubtitleText_withAirplaneModeOn_returnNull() {
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
 
         assertThat(mInternetDialogController.getSubtitleText(false)).isNull();
     }
 
     @Test
     public void getSubtitleText_withWifiOff_returnWifiIsOff() {
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
         when(mWifiManager.isWifiEnabled()).thenReturn(false);
 
         assertThat(mInternetDialogController.getSubtitleText(false))
@@ -263,7 +259,7 @@
 
     @Test
     public void getSubtitleText_withNoWifiEntry_returnSearchWifi() {
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
         mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
 
@@ -280,7 +276,7 @@
     @Test
     public void getSubtitleText_withWifiEntry_returnTapToConnect() {
         // The preconditions WiFi Entries is already in setUp()
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
 
         assertThat(mInternetDialogController.getSubtitleText(false))
@@ -295,7 +291,7 @@
 
     @Test
     public void getSubtitleText_deviceLockedWithWifiOn_returnUnlockToViewNetworks() {
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
 
@@ -305,7 +301,7 @@
 
     @Test
     public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
         mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
 
@@ -319,7 +315,7 @@
 
     @Test
     public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
         when(mWifiManager.isWifiEnabled()).thenReturn(true);
         mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
 
@@ -420,7 +416,7 @@
     @Test
     public void onAccessPointsChanged_oneConnectedEntry_callbackConnectedEntryOnly() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
 
@@ -433,7 +429,7 @@
     @Test
     public void onAccessPointsChanged_noConnectedEntryAndOneOther_callbackWifiEntriesOnly() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mWifiEntry1);
 
@@ -448,7 +444,7 @@
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndOneOther_callbackCorrectly() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -463,7 +459,7 @@
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndTwoOthers_callbackCorrectly() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -480,7 +476,7 @@
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndThreeOthers_callbackCutMore() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -497,7 +493,7 @@
 
         // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
 
         mInternetDialogController.onAccessPointsChanged(mAccessPoints);
 
@@ -508,7 +504,7 @@
     @Test
     public void onAccessPointsChanged_oneConnectedEntryAndFourOthers_callbackCutMore() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
@@ -526,7 +522,7 @@
 
         // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
 
         mInternetDialogController.onAccessPointsChanged(mAccessPoints);
 
@@ -537,7 +533,7 @@
     @Test
     public void onAccessPointsChanged_fourWifiEntries_callbackCutMore() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(true);
+        fakeAirplaneModeEnabled(true);
         mAccessPoints.clear();
         mAccessPoints.add(mWifiEntry1);
         mAccessPoints.add(mWifiEntry2);
@@ -566,7 +562,7 @@
 
         // Turn off airplane mode to has carrier network, then Wi-Fi entries will cut last one.
         reset(mInternetDialogCallback);
-        mInternetDialogController.setAirplaneModeEnabled(false);
+        fakeAirplaneModeEnabled(false);
 
         mInternetDialogController.onAccessPointsChanged(mAccessPoints);
 
@@ -576,6 +572,23 @@
     }
 
     @Test
+    public void onAccessPointsChanged_wifiIsDefaultButNoInternetAccess_putIntoWifiEntries() {
+        reset(mInternetDialogCallback);
+        mAccessPoints.clear();
+        when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
+        when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
+        when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
+        mAccessPoints.add(mWifiEntry1);
+
+        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+
+        mWifiEntries.clear();
+        mWifiEntries.add(mWifiEntry1);
+        verify(mInternetDialogCallback)
+                .onAccessPointsChanged(mWifiEntries, null /* connectedEntry */);
+    }
+
+    @Test
     public void setMergedCarrierWifiEnabledIfNeed_carrierProvisionsEnabled_doNothing() {
         when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
                 .thenReturn(true);
@@ -641,38 +654,7 @@
                 mContext.getPackageName());
     }
 
-    private class MockInternetDialogController extends InternetDialogController {
-
-        private GlobalSettings mGlobalSettings;
-        private boolean mIsAirplaneModeOn;
-
-        MockInternetDialogController(Context context, UiEventLogger uiEventLogger,
-                ActivityStarter starter, AccessPointController accessPointController,
-                SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
-                @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
-                @Main Handler handler, @Main Executor mainExecutor,
-                BroadcastDispatcher broadcastDispatcher,
-                KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings,
-                KeyguardStateController keyguardStateController, WindowManager windowManager,
-                ToastFactory toastFactory, Handler workerHandler,
-                CarrierConfigTracker carrierConfigTracker,
-                LocationController locationController,
-                DialogLaunchAnimator dialogLaunchAnimator) {
-            super(context, uiEventLogger, starter, accessPointController, subscriptionManager,
-                    telephonyManager, wifiManager, connectivityManager, handler, mainExecutor,
-                    broadcastDispatcher, keyguardUpdateMonitor, globalSettings,
-                    keyguardStateController, windowManager, toastFactory, workerHandler,
-                    carrierConfigTracker, locationController, dialogLaunchAnimator);
-            mGlobalSettings = globalSettings;
-        }
-
-        @Override
-        boolean isAirplaneModeEnabled() {
-            return mIsAirplaneModeOn;
-        }
-
-        public void setAirplaneModeEnabled(boolean enabled) {
-            mIsAirplaneModeOn = enabled;
-        }
+    private void fakeAirplaneModeEnabled(boolean enabled) {
+        when(mGlobalSettings.getInt(eq(AIRPLANE_MODE_ON), anyInt())).thenReturn(enabled ? 1 : 0);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index a39971d..9f152e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -269,6 +269,21 @@
     }
 
     @Test
+    public void testDisableWiFiWithVcnWithUnderlyingWifi() {
+        String testSsid = "Test VCN SSID";
+        setWifiEnabled(true);
+        verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
+
+        mNetworkController.setNoNetworksAvailable(false);
+        setWifiStateForVcn(true, testSsid);
+        setWifiLevelForVcn(1);
+        verifyLastMobileDataIndicatorsForVcn(true, 1, TelephonyIcons.ICON_CWF, false);
+
+        setWifiEnabled(false);
+        verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
+    }
+
+    @Test
     public void testCallStrengh() {
         if (true) return;
         String testSsid = "Test SSID";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
new file mode 100644
index 0000000..db7a8516
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.unfold.util
+
+import android.animation.ValueAnimator
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+import com.android.systemui.util.mockito.any
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var contentResolver: ContentResolver
+
+    @Mock
+    lateinit var sourceProvider: UnfoldTransitionProgressProvider
+
+    @Mock
+    lateinit var sinkProvider: TransitionProgressListener
+
+    lateinit var progressProvider: ScaleAwareTransitionProgressProvider
+
+    private val sourceProviderListenerCaptor =
+            ArgumentCaptor.forClass(TransitionProgressListener::class.java)
+
+    private val animatorDurationScaleListenerCaptor =
+            ArgumentCaptor.forClass(ContentObserver::class.java)
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        progressProvider = ScaleAwareTransitionProgressProvider(
+                sourceProvider,
+                contentResolver
+        )
+
+        verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture())
+        verify(contentResolver).registerContentObserver(any(), any(),
+                animatorDurationScaleListenerCaptor.capture())
+
+        progressProvider.addCallback(sinkProvider)
+    }
+
+    @Test
+    fun onTransitionStarted_animationsEnabled_eventReceived() {
+        setAnimationsEnabled(true)
+
+        source.onTransitionStarted()
+
+        verify(sinkProvider).onTransitionStarted()
+    }
+
+    @Test
+    fun onTransitionStarted_animationsNotEnabled_eventNotReceived() {
+        setAnimationsEnabled(false)
+
+        source.onTransitionStarted()
+
+        verifyNoMoreInteractions(sinkProvider)
+    }
+
+    @Test
+    fun onTransitionEnd_animationsEnabled_eventReceived() {
+        setAnimationsEnabled(true)
+
+        source.onTransitionFinished()
+
+        verify(sinkProvider).onTransitionFinished()
+    }
+
+    @Test
+    fun onTransitionEnd_animationsNotEnabled_eventNotReceived() {
+        setAnimationsEnabled(false)
+
+        source.onTransitionFinished()
+
+        verifyNoMoreInteractions(sinkProvider)
+    }
+
+    @Test
+    fun onTransitionProgress_animationsEnabled_eventReceived() {
+        setAnimationsEnabled(true)
+
+        source.onTransitionProgress(42f)
+
+        verify(sinkProvider).onTransitionProgress(42f)
+    }
+
+    @Test
+    fun onTransitionProgress_animationsNotEnabled_eventNotReceived() {
+        setAnimationsEnabled(false)
+
+        source.onTransitionProgress(42f)
+
+        verifyNoMoreInteractions(sinkProvider)
+    }
+
+    private fun setAnimationsEnabled(enabled: Boolean) {
+        val durationScale = if (enabled) {
+            1f
+        } else {
+            0f
+        }
+        ValueAnimator.setDurationScale(durationScale)
+        animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
+    }
+
+    private val source: TransitionProgressListener
+        get() = sourceProviderListenerCaptor.value
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ba11e9c..7a70bfe 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24299,24 +24299,24 @@
         }
         enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                 true /* checkShell */, "stop package");
-        boolean shouldUnhibernate = false;
         // writer
         synchronized (mLock) {
             final PackageSetting ps = mSettings.getPackageLPr(packageName);
-            if (ps != null && ps.getStopped(userId) && !stopped) {
-                shouldUnhibernate = true;
-            }
             if (!shouldFilterApplicationLocked(ps, callingUid, userId)
                     && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, userId)) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
-        if (shouldUnhibernate) {
+        // If this would cause the app to leave force-stop, then also make sure to unhibernate the
+        // app if needed.
+        if (!stopped) {
             mHandler.post(() -> {
                 AppHibernationManagerInternal ah =
                         mInjector.getLocalService(AppHibernationManagerInternal.class);
-                ah.setHibernatingForUser(packageName, userId, false);
-                ah.setHibernatingGlobally(packageName, false);
+                if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
+                    ah.setHibernatingForUser(packageName, userId, false);
+                    ah.setHibernatingGlobally(packageName, false);
+                }
             });
         }
     }
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 2be29d4..dfff76d 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -50,6 +50,10 @@
 import static android.util.MathUtils.constrain;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
 import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__NOT_OPPORTUNISTIC;
 import static com.android.internal.util.FrameworkStatsLog.DATA_USAGE_BYTES_TRANSFER__OPPORTUNISTIC_DATA_SUB__OPPORTUNISTIC;
 import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__GEO;
@@ -4387,8 +4391,9 @@
                 final int userId = userInfo.getUserHandle().getIdentifier();
 
                 if (isAccessibilityShortcutUser(mContext, userId)) {
-                    final int software_shortcut_type = Settings.Secure.getIntForUser(resolver,
-                            Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId);
+                    final int software_shortcut_type = convertToAccessibilityShortcutType(
+                            Settings.Secure.getIntForUser(resolver,
+                                    Settings.Secure.ACCESSIBILITY_BUTTON_MODE, 0, userId));
                     final String software_shortcut_list = Settings.Secure.getStringForUser(resolver,
                             Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, userId);
                     final int software_shortcut_service_num = countAccessibilityServices(
@@ -4509,6 +4514,19 @@
                 && !TextUtils.isEmpty(software_string);
     }
 
+    private int convertToAccessibilityShortcutType(int shortcutType) {
+        switch (shortcutType) {
+            case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR:
+                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_BUTTON;
+            case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU:
+                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_FLOATING_MENU;
+            case Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE:
+                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_GESTURE;
+            default:
+                return ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__UNKNOWN_TYPE;
+        }
+    }
+
     // Thermal event received from vendor thermal management subsystem
     private static final class ThermalEventListener extends IThermalEventListener.Stub {
         @Override
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 7dec4e7..51c3b33 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -77,6 +77,7 @@
 import android.os.PowerManager.WakeLock;
 import android.os.Process;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Slog;
 
@@ -782,8 +783,19 @@
             // TODO(b/179091925): Move the delayed-message handling to BaseState
 
             // If underlying is null, all underlying networks have been lost. Disconnect VCN after a
-            // timeout.
+            // timeout (or immediately if in airplane mode, since the device user has indicated that
+            // the radios should all be turned off).
             if (underlying == null) {
+                if (mDeps.isAirplaneModeOn(mVcnContext)) {
+                    sendMessageAndAcquireWakeLock(
+                            EVENT_UNDERLYING_NETWORK_CHANGED,
+                            TOKEN_ALL,
+                            new EventUnderlyingNetworkChangedInfo(null));
+                    sendDisconnectRequestedAndAcquireWakelock(
+                            DISCONNECT_REASON_UNDERLYING_NETWORK_LOST, false /* shouldQuit */);
+                    return;
+                }
+
                 setDisconnectRequestAlarm();
             } else {
                 // Received a new Network so any previous alarm is irrelevant - cancel + clear it,
@@ -2414,6 +2426,12 @@
                     validationStatusCallback);
         }
 
+        /** Checks if airplane mode is enabled. */
+        public boolean isAirplaneModeOn(@NonNull VcnContext vcnContext) {
+            return Settings.Global.getInt(vcnContext.getContext().getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+        }
+
         /** Gets the elapsed real time since boot, in millis. */
         public long getElapsedRealTime() {
             return SystemClock.elapsedRealtime();
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
index fd97557..e053dc3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceHibernationTests.kt
@@ -29,6 +29,7 @@
 import com.android.server.apphibernation.AppHibernationService
 import com.android.server.extendedtestutils.wheneverStatic
 import com.android.server.testutils.whenever
+import org.junit.Assert
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
@@ -100,8 +101,11 @@
             rule.system().dataAppDirectory)
         val pm = createPackageManagerService()
         rule.system().validateFinalState()
-        val ps = pm.getPackageSetting(TEST_PACKAGE_NAME)
-        ps!!.setStopped(true, TEST_USER_ID)
+
+        TestableLooper.get(this).processAllMessages()
+
+        whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+            .thenReturn(true)
 
         pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
 
@@ -112,6 +116,31 @@
     }
 
     @Test
+    fun testExitForceStop_nonExistingAppHibernationManager_doesNotThrowException() {
+        whenever(rule.mocks().injector.getLocalService(AppHibernationManagerInternal::class.java))
+            .thenReturn(null)
+
+        rule.system().stageScanExistingPackage(
+            TEST_PACKAGE_NAME,
+            1L,
+            rule.system().dataAppDirectory)
+        val pm = createPackageManagerService()
+        rule.system().validateFinalState()
+
+        TestableLooper.get(this).processAllMessages()
+
+        whenever(appHibernationManager.isHibernatingForUser(TEST_PACKAGE_NAME, TEST_USER_ID))
+            .thenReturn(true)
+
+        try {
+            pm.setPackageStoppedState(TEST_PACKAGE_NAME, false, TEST_USER_ID)
+            TestableLooper.get(this).processAllMessages()
+        } catch (e: Exception) {
+            Assert.fail("Method throws exception when AppHibernationManager is not ready.\n$e")
+        }
+    }
+
+    @Test
     fun testGetOptimizablePackages_ExcludesGloballyHibernatingPackages() {
         rule.system().stageScanExistingPackage(
             TEST_PACKAGE_NAME,
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index c9a8947a..7b5f0b1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -118,6 +118,8 @@
 
     @Test
     public void testNullNetworkDoesNotTriggerDisconnect() throws Exception {
+        doReturn(false).when(mDeps).isAirplaneModeOn(any());
+
         mGatewayConnection
                 .getUnderlyingNetworkTrackerCallback()
                 .onSelectedUnderlyingNetworkChanged(null);
@@ -129,6 +131,19 @@
     }
 
     @Test
+    public void testNullNetworkAirplaneModeDisconnects() throws Exception {
+        doReturn(true).when(mDeps).isAirplaneModeOn(any());
+
+        mGatewayConnection
+                .getUnderlyingNetworkTrackerCallback()
+                .onSelectedUnderlyingNetworkChanged(null);
+        mTestLooper.dispatchAll();
+
+        assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+        verify(mIkeSession).kill();
+    }
+
+    @Test
     public void testNewNetworkTriggersMigration() throws Exception {
         mGatewayConnection
                 .getUnderlyingNetworkTrackerCallback()