Merge "[Media Rec] Update the background to be colorSurface." into tm-dev
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 53a3889..c572f82 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -32,8 +32,6 @@
 import android.annotation.UserHandleAware;
 import android.content.Context;
 
-import com.android.internal.util.FrameworkStatsLog;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
@@ -453,62 +451,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ReasonCode {}
 
-    private static final int EXEMPTION_REASON_SYSTEM_UID = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_SYSTEM_UID;
-    private static final int EXEMPTION_REASON_ALLOWLISTED_PACKAGE = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_ALLOWLISTED_PACKAGE;
-    private static final int EXEMPTION_REASON_COMPANION_DEVICE_MANAGER = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_COMPANION_DEVICE_MANAGER;
-    private static final int EXEMPTION_REASON_DEVICE_DEMO_MODE = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DEVICE_DEMO_MODE;
-    private static final int EXEMPTION_REASON_DEVICE_OWNER = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DEVICE_OWNER;
-    private static final int EXEMPTION_REASON_PROFILE_OWNER = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_PROFILE_OWNER;
-    private static final int EXEMPTION_REASON_PROC_STATE_PERSISTENT = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_PROC_STATE_PERSISTENT;
-    private static final int EXEMPTION_REASON_PROC_STATE_PERSISTENT_UI = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_PROC_STATE_PERSISTENT_UI;
-    private static final int EXEMPTION_REASON_OP_ACTIVATE_VPN = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_OP_ACTIVATE_VPN;
-    private static final int EXEMPTION_REASON_OP_ACTIVATE_PLATFORM_VPN = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_OP_ACTIVATE_PLATFORM_VPN;
-    private static final int EXEMPTION_REASON_SYSTEM_MODULE = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_SYSTEM_MODULE;
-    private static final int EXEMPTION_REASON_CARRIER_PRIVILEGED_APP = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_CARRIER_PRIVILEGED_APP;
-    private static final int EXEMPTION_REASON_SYSTEM_ALLOW_LISTED = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_SYSTEM_ALLOW_LISTED;
-    private static final int EXEMPTION_REASON_ROLE_DIALER = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_ROLE_DIALER;
-    private static final int EXEMPTION_REASON_ROLE_EMERGENCY = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_ROLE_EMERGENCY;
-    private static final int EXEMPTION_REASON_DENIED = FrameworkStatsLog
-            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DENIED;
-    /**
-     * List of exemption reason codes used for statsd logging in AppBackgroundRestrictionsInfo atom.
-     * @hide
-     */
-    @IntDef(prefix = { "EXEMPTION_REASON_" }, value = {
-            EXEMPTION_REASON_SYSTEM_UID,
-            EXEMPTION_REASON_ALLOWLISTED_PACKAGE,
-            EXEMPTION_REASON_COMPANION_DEVICE_MANAGER,
-            EXEMPTION_REASON_DEVICE_DEMO_MODE,
-            EXEMPTION_REASON_DEVICE_OWNER,
-            EXEMPTION_REASON_PROFILE_OWNER,
-            EXEMPTION_REASON_PROC_STATE_PERSISTENT,
-            EXEMPTION_REASON_PROC_STATE_PERSISTENT_UI,
-            EXEMPTION_REASON_OP_ACTIVATE_VPN,
-            EXEMPTION_REASON_OP_ACTIVATE_PLATFORM_VPN,
-            EXEMPTION_REASON_SYSTEM_MODULE,
-            EXEMPTION_REASON_CARRIER_PRIVILEGED_APP,
-            EXEMPTION_REASON_SYSTEM_ALLOW_LISTED,
-            EXEMPTION_REASON_ROLE_DIALER,
-            EXEMPTION_REASON_ROLE_EMERGENCY,
-            EXEMPTION_REASON_DENIED,
-    })
-    public @interface ExemptionReason {}
-
     /**
      * @hide
      */
@@ -679,40 +621,40 @@
      * @hide
      * @return the reason code mapped to statsd for the AppBackgroundRestrictionsInfo atom.
      */
-    public static @ExemptionReason int getExemptionReasonForStatsd(@ReasonCode int reasonCode) {
+    public static int getExemptionReasonForStatsd(@ReasonCode int reasonCode) {
         switch (reasonCode) {
             case REASON_SYSTEM_UID:
-                return EXEMPTION_REASON_SYSTEM_UID;
+                return AppBackgroundRestrictionsInfo.REASON_SYSTEM_UID;
             case REASON_ALLOWLISTED_PACKAGE:
-                return EXEMPTION_REASON_ALLOWLISTED_PACKAGE;
+                return AppBackgroundRestrictionsInfo.REASON_ALLOWLISTED_PACKAGE;
             case REASON_COMPANION_DEVICE_MANAGER:
-                return EXEMPTION_REASON_COMPANION_DEVICE_MANAGER;
+                return AppBackgroundRestrictionsInfo.REASON_COMPANION_DEVICE_MANAGER;
             case REASON_DEVICE_DEMO_MODE:
-                return EXEMPTION_REASON_DEVICE_DEMO_MODE;
+                return AppBackgroundRestrictionsInfo.REASON_DEVICE_DEMO_MODE;
             case REASON_DEVICE_OWNER:
-                return EXEMPTION_REASON_DEVICE_OWNER;
+                return AppBackgroundRestrictionsInfo.REASON_DEVICE_OWNER;
             case REASON_PROFILE_OWNER:
-                return EXEMPTION_REASON_PROFILE_OWNER;
+                return AppBackgroundRestrictionsInfo.REASON_PROFILE_OWNER;
             case REASON_PROC_STATE_PERSISTENT:
-                return EXEMPTION_REASON_PROC_STATE_PERSISTENT;
+                return AppBackgroundRestrictionsInfo.REASON_PROC_STATE_PERSISTENT;
             case REASON_PROC_STATE_PERSISTENT_UI:
-                return EXEMPTION_REASON_PROC_STATE_PERSISTENT_UI;
+                return AppBackgroundRestrictionsInfo.REASON_PROC_STATE_PERSISTENT_UI;
             case REASON_OP_ACTIVATE_VPN:
-                return EXEMPTION_REASON_OP_ACTIVATE_VPN;
+                return AppBackgroundRestrictionsInfo.REASON_OP_ACTIVATE_VPN;
             case REASON_OP_ACTIVATE_PLATFORM_VPN:
-                return EXEMPTION_REASON_OP_ACTIVATE_PLATFORM_VPN;
+                return AppBackgroundRestrictionsInfo.REASON_OP_ACTIVATE_PLATFORM_VPN;
             case REASON_SYSTEM_MODULE:
-                return EXEMPTION_REASON_SYSTEM_MODULE;
+                return AppBackgroundRestrictionsInfo.REASON_SYSTEM_MODULE;
             case REASON_CARRIER_PRIVILEGED_APP:
-                return EXEMPTION_REASON_CARRIER_PRIVILEGED_APP;
+                return AppBackgroundRestrictionsInfo.REASON_CARRIER_PRIVILEGED_APP;
             case REASON_SYSTEM_ALLOW_LISTED:
-                return EXEMPTION_REASON_SYSTEM_ALLOW_LISTED;
+                return AppBackgroundRestrictionsInfo.REASON_SYSTEM_ALLOW_LISTED;
             case REASON_ROLE_DIALER:
-                return EXEMPTION_REASON_ROLE_DIALER;
+                return AppBackgroundRestrictionsInfo.REASON_ROLE_DIALER;
             case REASON_ROLE_EMERGENCY:
-                return EXEMPTION_REASON_ROLE_EMERGENCY;
+                return AppBackgroundRestrictionsInfo.REASON_ROLE_EMERGENCY;
             default:
-                return EXEMPTION_REASON_DENIED;
+                return AppBackgroundRestrictionsInfo.REASON_DENIED;
         }
     }
 
diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java
index 647a266..79bac9b 100644
--- a/core/java/android/accessibilityservice/InputMethod.java
+++ b/core/java/android/accessibilityservice/InputMethod.java
@@ -181,14 +181,14 @@
 
     final void startInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
         Log.v(LOG_TAG, "startInput(): editor=" + attribute);
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.startInput");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.startInput");
         doStartInput(ic, attribute, false /* restarting */);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
 
     final void restartInput(@Nullable InputConnection ic, @NonNull EditorInfo attribute) {
         Log.v(LOG_TAG, "restartInput(): editor=" + attribute);
-        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AccessibilityService.restartInput");
         doStartInput(ic, attribute, true /* restarting */);
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 6be6f9d..96b1abb 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1274,6 +1274,7 @@
     private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener {
         private final int mRtSurfaceWidth;
         private final int mRtSurfaceHeight;
+        private boolean mRtFirst = true;
         private final SurfaceControl.Transaction mPositionChangedTransaction =
                 new SurfaceControl.Transaction();
 
@@ -1284,14 +1285,15 @@
 
         @Override
         public void positionChanged(long frameNumber, int left, int top, int right, int bottom) {
-            if (mRTLastReportedPosition.left == left
+            if (!mRtFirst && (mRTLastReportedPosition.left == left
                     && mRTLastReportedPosition.top == top
                     && mRTLastReportedPosition.right == right
                     && mRTLastReportedPosition.bottom == bottom
                     && mRTLastReportedSurfaceSize.x == mRtSurfaceWidth
-                    && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight) {
+                    && mRTLastReportedSurfaceSize.y == mRtSurfaceHeight)) {
                 return;
             }
+            mRtFirst = false;
             try {
                 if (DEBUG_POSITION) {
                     Log.d(TAG, String.format(
diff --git a/core/java/android/window/BackEvent.java b/core/java/android/window/BackEvent.java
index 14985c9..1024e2e 100644
--- a/core/java/android/window/BackEvent.java
+++ b/core/java/android/window/BackEvent.java
@@ -46,8 +46,8 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface SwipeEdge{}
 
-    private final int mTouchX;
-    private final int mTouchY;
+    private final float mTouchX;
+    private final float mTouchY;
     private final float mProgress;
 
     @SwipeEdge
@@ -58,14 +58,14 @@
     /**
      * Creates a new {@link BackEvent} instance.
      *
-     * @param touchX Absolute X location of the touch point.
-     * @param touchY Absolute Y location of the touch point.
+     * @param touchX Absolute X location of the touch point of this event.
+     * @param touchY Absolute Y location of the touch point of this event.
      * @param progress Value between 0 and 1 on how far along the back gesture is.
      * @param swipeEdge Indicates which edge the swipe starts from.
      * @param departingAnimationTarget The remote animation target of the departing application
      *                                 window.
      */
-    public BackEvent(int touchX, int touchY, float progress, @SwipeEdge int swipeEdge,
+    public BackEvent(float touchX, float touchY, float progress, @SwipeEdge int swipeEdge,
             @Nullable RemoteAnimationTarget departingAnimationTarget) {
         mTouchX = touchX;
         mTouchY = touchY;
@@ -75,8 +75,8 @@
     }
 
     private BackEvent(@NonNull Parcel in) {
-        mTouchX = in.readInt();
-        mTouchY = in.readInt();
+        mTouchX = in.readFloat();
+        mTouchY = in.readFloat();
         mProgress = in.readFloat();
         mSwipeEdge = in.readInt();
         mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
@@ -101,8 +101,8 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mTouchX);
-        dest.writeInt(mTouchY);
+        dest.writeFloat(mTouchX);
+        dest.writeFloat(mTouchY);
         dest.writeFloat(mProgress);
         dest.writeInt(mSwipeEdge);
         dest.writeTypedObject(mDepartingAnimationTarget, flags);
@@ -118,14 +118,14 @@
     /**
      * Returns the absolute X location of the touch point.
      */
-    public int getTouchX() {
+    public float getTouchX() {
         return mTouchX;
     }
 
     /**
      * Returns the absolute Y location of the touch point.
      */
-    public int getTouchY() {
+    public float getTouchY() {
         return mTouchY;
     }
 
diff --git a/core/java/com/android/internal/policy/ForceShowNavigationBarSettingsObserver.java b/core/java/com/android/internal/policy/KidsModeSettingsObserver.java
similarity index 85%
rename from core/java/com/android/internal/policy/ForceShowNavigationBarSettingsObserver.java
rename to core/java/com/android/internal/policy/KidsModeSettingsObserver.java
index 75dce5a..8a1d407 100644
--- a/core/java/com/android/internal/policy/ForceShowNavigationBarSettingsObserver.java
+++ b/core/java/com/android/internal/policy/KidsModeSettingsObserver.java
@@ -24,17 +24,17 @@
 import android.provider.Settings;
 
 /**
- * A ContentObserver for listening force show navigation bar relative setting keys:
+ * A ContentObserver for listening kids mode relative setting keys:
  *  - {@link Settings.Secure#NAVIGATION_MODE}
- *  - {@link Settings.Secure#NAV_BAR_FORCE_VISIBLE}
+ *  - {@link Settings.Secure#NAV_BAR_KIDS_MODE}
  *
  * @hide
  */
-public class ForceShowNavigationBarSettingsObserver extends ContentObserver {
+public class KidsModeSettingsObserver extends ContentObserver {
     private Context mContext;
     private Runnable mOnChangeRunnable;
 
-    public ForceShowNavigationBarSettingsObserver(Handler handler, Context context) {
+    public KidsModeSettingsObserver(Handler handler, Context context) {
         super(handler);
         mContext = context;
     }
@@ -52,7 +52,7 @@
                 Settings.Secure.getUriFor(Settings.Secure.NAVIGATION_MODE),
                 false, this, UserHandle.USER_ALL);
         r.registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_FORCE_VISIBLE),
+                Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE),
                 false, this, UserHandle.USER_ALL);
     }
 
@@ -78,6 +78,6 @@
         return Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.NAVIGATION_MODE, 0, UserHandle.USER_CURRENT) == 0
                 && Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.NAV_BAR_FORCE_VISIBLE, 0, UserHandle.USER_CURRENT) == 1;
+                Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1;
     }
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4284e7e..757f409 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5682,8 +5682,8 @@
          restricted level.
     -->
     <array name="config_bg_current_drain_threshold_to_bg_restricted">
-        <item>10.0</item> <!-- regular device -->
-        <item>20.0</item> <!-- low ram device -->
+        <item>4.0</item> <!-- regular device -->
+        <item>8.0</item> <!-- low ram device -->
     </array>
 
     <!-- The background current drain monitoring window size. -->
@@ -5744,6 +5744,12 @@
     -->
     <bool name="config_bg_prompt_fgs_with_noti_to_bg_restricted">false</bool>
 
+    <!-- The behavior when the system detects it's abusive, should the system prompt the user
+         to put it into the bg restricted level.
+         True - we'll show the prompt to user, False - we'll not show it.
+    -->
+    <bool name="config_bg_prompt_abusive_apps_to_bg_restricted">false</bool>
+
     <!-- The types of state where we'll exempt its battery usage during that state.
          The state here must be one or a combination of STATE_TYPE_* in BaseAppStateTracker.
     -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 41d340f..3334822 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4765,6 +4765,7 @@
   <java-symbol type="integer" name="config_bg_current_drain_media_playback_min_duration" />
   <java-symbol type="integer" name="config_bg_current_drain_location_min_duration" />
   <java-symbol type="bool" name="config_bg_prompt_fgs_with_noti_to_bg_restricted" />
+  <java-symbol type="bool" name="config_bg_prompt_abusive_apps_to_bg_restricted" />
   <java-symbol type="integer" name="config_bg_current_drain_exempted_types" />
   <java-symbol type="bool" name="config_bg_current_drain_high_threshold_by_bg_location" />
   <java-symbol type="drawable" name="ic_swap_horiz" />
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 42ac195..cfd0624 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -301,7 +301,8 @@
         int backType = mBackNavigationInfo.getType();
         RemoteAnimationTarget animationTarget = mBackNavigationInfo.getDepartingAnimationTarget();
 
-        BackEvent backEvent = new BackEvent(0, 0, progress, swipeEdge, animationTarget);
+        BackEvent backEvent = new BackEvent(
+                event.getX(), event.getY(), progress, swipeEdge, animationTarget);
         IOnBackInvokedCallback targetCallback = null;
         if (shouldDispatchToLauncher(backType)) {
             targetCallback = mBackToLauncherCallback;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index b6fb828..5ef2413 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -385,7 +385,9 @@
                 mMainExecutor.execute(() -> {
                     int expandedId = INVALID_TASK_ID;
                     if (mStackView != null && mStackView.getExpandedBubble() != null
-                            && isStackExpanded() && !mStackView.isExpansionAnimating()) {
+                            && isStackExpanded()
+                            && !mStackView.isExpansionAnimating()
+                            && !mStackView.isSwitchAnimating()) {
                         expandedId = mStackView.getExpandedBubble().getTaskId();
                     }
                     if (expandedId != INVALID_TASK_ID && expandedId != taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index cb88203..b7c5eb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1608,6 +1608,13 @@
     }
 
     /**
+     * Whether the stack of bubbles is animating a switch between bubbles.
+     */
+    public boolean isSwitchAnimating() {
+        return mIsBubbleSwitchAnimating;
+    }
+
+    /**
      * The {@link Bubble} that is expanded, null if one does not exist.
      */
     @VisibleForTesting
@@ -2467,6 +2474,10 @@
 
     private void dismissBubbleIfExists(@Nullable BubbleViewProvider bubble) {
         if (bubble != null && mBubbleData.hasBubbleInStackWithKey(bubble.getKey())) {
+            if (mIsExpanded && mBubbleData.getBubbles().size() > 1) {
+                // If we have more than 1 bubble we will perform the switch animation
+                mIsBubbleSwitchAnimating = true;
+            }
             mBubbleData.dismissBubbleWithKey(bubble.getKey(), Bubbles.DISMISS_USER_GESTURE);
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
index 003c559..4bb5805 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizer.java
@@ -40,7 +40,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.ForceShowNavigationBarSettingsObserver;
+import com.android.internal.policy.KidsModeSettingsObserver;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayInsetsController;
@@ -85,7 +85,7 @@
     private int mDisplayWidth;
     private int mDisplayHeight;
 
-    private ForceShowNavigationBarSettingsObserver mForceShowNavigationBarSettingsObserver;
+    private KidsModeSettingsObserver mKidsModeSettingsObserver;
     private boolean mEnabled;
 
     DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
@@ -138,14 +138,14 @@
             DisplayController displayController,
             DisplayInsetsController displayInsetsController,
             Optional<RecentTasksController> recentTasks,
-            ForceShowNavigationBarSettingsObserver forceShowNavigationBarSettingsObserver) {
+            KidsModeSettingsObserver kidsModeSettingsObserver) {
         super(taskOrganizerController, mainExecutor, context, /* compatUI= */ null, recentTasks);
         mContext = context;
         mMainHandler = mainHandler;
         mSyncQueue = syncTransactionQueue;
         mDisplayController = displayController;
         mDisplayInsetsController = displayInsetsController;
-        mForceShowNavigationBarSettingsObserver = forceShowNavigationBarSettingsObserver;
+        mKidsModeSettingsObserver = kidsModeSettingsObserver;
     }
 
     public KidsModeTaskOrganizer(
@@ -169,13 +169,13 @@
      */
     public void initialize(StartingWindowController startingWindowController) {
         initStartingWindow(startingWindowController);
-        if (mForceShowNavigationBarSettingsObserver == null) {
-            mForceShowNavigationBarSettingsObserver = new ForceShowNavigationBarSettingsObserver(
+        if (mKidsModeSettingsObserver == null) {
+            mKidsModeSettingsObserver = new KidsModeSettingsObserver(
                     mMainHandler, mContext);
         }
-        mForceShowNavigationBarSettingsObserver.setOnChangeRunnable(() -> updateKidsModeState());
+        mKidsModeSettingsObserver.setOnChangeRunnable(() -> updateKidsModeState());
         updateKidsModeState();
-        mForceShowNavigationBarSettingsObserver.register();
+        mKidsModeSettingsObserver.register();
     }
 
     @Override
@@ -211,7 +211,7 @@
 
     @VisibleForTesting
     void updateKidsModeState() {
-        final boolean enabled = mForceShowNavigationBarSettingsObserver.isEnabled();
+        final boolean enabled = mKidsModeSettingsObserver.isEnabled();
         if (mEnabled == enabled) {
             return;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b6635f3..9dc861c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -924,6 +924,7 @@
             removeContentOverlay(mSwipePipToHomeOverlay, null /* callback */);
             mSwipePipToHomeOverlay = null;
         }
+        resetShadowRadius();
         mPipTransitionState.setInSwipePipToHomeTransition(false);
         mPictureInPictureParams = null;
         mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
@@ -1569,13 +1570,28 @@
             // Avoid double removal, which is fatal.
             return;
         }
-        final SurfaceControl.Transaction tx =
-                mSurfaceControlTransactionFactory.getTransaction();
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
         tx.remove(surface);
         tx.apply();
         if (callback != null) callback.run();
     }
 
+    private void resetShadowRadius() {
+        if (mPipTransitionState.getTransitionState() == PipTransitionState.UNDEFINED) {
+            // mLeash is undefined when in PipTransitionState.UNDEFINED
+            return;
+        }
+        final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
+        tx.setShadowRadius(mLeash, 0f);
+        tx.apply();
+    }
+
+    @VisibleForTesting
+    public void setSurfaceControlTransactionFactory(
+            PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
+        mSurfaceControlTransactionFactory = factory;
+    }
+
     /**
      * Dumps internal states.
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 10dfdc3..dd2634c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -414,7 +414,7 @@
     }
 
     RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) {
-        if (ENABLE_SHELL_TRANSITIONS || apps.length < 2) return null;
+        if (ENABLE_SHELL_TRANSITIONS || !isSplitScreenVisible()) return null;
         // TODO(b/206487881): Integrate this with shell transition.
         SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
         if (mSplitTasksContainerLayer != null) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
index ff6dfdb..5526d5b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java
@@ -44,7 +44,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.policy.ForceShowNavigationBarSettingsObserver;
+import com.android.internal.policy.KidsModeSettingsObserver;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayInsetsController;
 import com.android.wm.shell.common.ShellExecutor;
@@ -72,7 +72,7 @@
     @Mock private SurfaceControl mLeash;
     @Mock private WindowContainerToken mToken;
     @Mock private WindowContainerTransaction mTransaction;
-    @Mock private ForceShowNavigationBarSettingsObserver mObserver;
+    @Mock private KidsModeSettingsObserver mObserver;
     @Mock private StartingWindowController mStartingWindowController;
     @Mock private DisplayInsetsController mDisplayInsetsController;
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 8ef1df6..c685fdc 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -30,7 +30,6 @@
 import static org.mockito.Mockito.verify;
 
 import android.app.TaskInfo;
-import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -104,7 +103,7 @@
         final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null,
                         TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
-        oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+        oldAnimator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
         oldAnimator.start();
 
         final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
@@ -134,7 +133,7 @@
 
     @Test
     public void pipTransitionAnimator_rotatedEndValue() {
-        final DummySurfaceControlTx tx = new DummySurfaceControlTx();
+        final PipDummySurfaceControlTx tx = new PipDummySurfaceControlTx();
         final Rect startBounds = new Rect(200, 700, 400, 800);
         final Rect endBounds = new Rect(0, 0, 500, 1000);
         // Fullscreen to PiP.
@@ -184,7 +183,7 @@
         final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
                 .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null,
                         TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0);
-        animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
+        animator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
 
         animator.setPipAnimationCallback(mPipAnimationCallback);
 
@@ -201,44 +200,4 @@
         verify(mPipAnimationCallback).onPipAnimationEnd(eq(mTaskInfo),
                 any(SurfaceControl.Transaction.class), eq(animator));
     }
-
-    /**
-     * A dummy {@link SurfaceControl.Transaction} class.
-     * This is created as {@link Mock} does not support method chaining.
-     */
-    public static class DummySurfaceControlTx extends SurfaceControl.Transaction {
-        @Override
-        public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
-            return this;
-        }
-
-        @Override
-        public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
-            return this;
-        }
-
-        @Override
-        public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
-            return this;
-        }
-
-        @Override
-        public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) {
-            return this;
-        }
-
-        @Override
-        public SurfaceControl.Transaction setMatrix(SurfaceControl leash, Matrix matrix,
-                float[] float9) {
-            return this;
-        }
-
-        @Override
-        public SurfaceControl.Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
-            return this;
-        }
-
-        @Override
-        public void apply() {}
-    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java
new file mode 100644
index 0000000..ccf8f6e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip;
+
+import android.graphics.Matrix;
+import android.view.SurfaceControl;
+
+/**
+ * A dummy {@link SurfaceControl.Transaction} class for testing purpose and supports
+ * method chaining.
+ */
+public class PipDummySurfaceControlTx extends SurfaceControl.Transaction {
+    @Override
+    public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setShadowRadius(SurfaceControl leash, float radius) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setMatrix(SurfaceControl leash, Matrix matrix,
+            float[] float9) {
+        return this;
+    }
+
+    @Override
+    public SurfaceControl.Transaction setFrameTimelineVsync(long frameTimelineVsyncId) {
+        return this;
+    }
+
+    @Override
+    public void apply() {}
+}
+
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 14d9fb9..def9ad2 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -242,6 +242,7 @@
         mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
                 mContext.getResources(), true, true));
         mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
+        mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new);
         doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
         doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
index 6535665..d97c819 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtils.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.UserManager;
 import android.util.Log;
 
@@ -37,13 +36,9 @@
      * @return whether the device is permitted to use Wi-Fi Tethering
      */
     public static boolean isWifiTetheringAllowed(Context context) {
-        final UserManager userManager = context.getSystemService(UserManager.class);
-        final Bundle restrictions = userManager.getUserRestrictions();
-        if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_WIFI_TETHERING)) {
-            Log.i(TAG, "Wi-Fi Tethering isn't available due to user restriction.");
-            return false;
-        }
-        return true;
+        if (!hasUserRestrictionFromT(context, UserManager.DISALLOW_WIFI_TETHERING)) return true;
+        Log.w(TAG, "Wi-Fi Tethering isn't available due to user restriction.");
+        return false;
     }
 
     /**
@@ -53,13 +48,9 @@
      * @return whether the device is permitted to use Wi-Fi Direct
      */
     public static boolean isWifiDirectAllowed(Context context) {
-        final UserManager userManager = context.getSystemService(UserManager.class);
-        final Bundle restrictions = userManager.getUserRestrictions();
-        if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)) {
-            Log.i(TAG, "Wi-Fi Direct isn't available due to user restriction.");
-            return false;
-        }
-        return true;
+        if (!hasUserRestrictionFromT(context, UserManager.DISALLOW_WIFI_DIRECT)) return true;
+        Log.w(TAG, "Wi-Fi Direct isn't available due to user restriction.");
+        return false;
     }
 
     /**
@@ -69,13 +60,9 @@
      * @return whether the device is permitted to add new Wi-Fi config
      */
     public static boolean isAddWifiConfigAllowed(Context context) {
-        final UserManager userManager = context.getSystemService(UserManager.class);
-        final Bundle restrictions = userManager.getUserRestrictions();
-        if (isAtLeastT() && restrictions.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)) {
-            Log.i(TAG, "Wi-Fi Add network isn't available due to user restriction.");
-            return false;
-        }
-        return true;
+        if (!hasUserRestrictionFromT(context, UserManager.DISALLOW_ADD_WIFI_CONFIG)) return true;
+        Log.w(TAG, "Wi-Fi Add network isn't available due to user restriction.");
+        return false;
     }
 
     /**
@@ -98,7 +85,7 @@
         return userManager.hasUserRestriction(restrictionKey);
     }
 
-    @ChecksSdkIntAtLeast(api=Build.VERSION_CODES.TIRAMISU)
+    @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
     private static boolean isAtLeastT() {
         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU;
     }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
index e9326dd..af9e69a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEnterpriseRestrictionUtilsTest.java
@@ -15,122 +15,91 @@
  */
 package com.android.settingslib.wifi;
 
+import static android.os.UserManager.DISALLOW_ADD_WIFI_CONFIG;
 import static android.os.UserManager.DISALLOW_CHANGE_WIFI_STATE;
+import static android.os.UserManager.DISALLOW_WIFI_DIRECT;
+import static android.os.UserManager.DISALLOW_WIFI_TETHERING;
 
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.UserManager;
 
 import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.util.ReflectionHelpers;
 
 @RunWith(RobolectricTestRunner.class)
 public class WifiEnterpriseRestrictionUtilsTest {
 
-    private Context mContext;
+    static final String SDK_INT = "SDK_INT";
+    static final int VERSION_CODES_S = Build.VERSION_CODES.S;
+    static final int VERSION_CODES_T = Build.VERSION_CODES.TIRAMISU;
+
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Spy
+    Context mContext = ApplicationProvider.getApplicationContext();
     @Mock
     private UserManager mUserManager;
-    @Mock
-    private Bundle mBundle;
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = spy(ApplicationProvider.getApplicationContext());
         when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
-        when(mUserManager.getUserRestrictions()).thenReturn(mBundle);
-        ReflectionHelpers.setStaticField(
-                Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        ReflectionHelpers.setStaticField(Build.VERSION.class, SDK_INT, VERSION_CODES_T);
     }
 
     @Test
-    public void isWifiTetheringAllowed_setSDKForS_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
-        when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_TETHERING)).thenReturn(true);
-
-        assertThat(WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mContext)).isTrue();
-    }
-
-    @Test
-    public void isWifiTetheringAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() {
-        ReflectionHelpers.setStaticField(
-                Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
-        when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_TETHERING)).thenReturn(true);
+    public void isWifiTetheringAllowed_hasDisallowRestriction_shouldReturnFalse() {
+        when(mUserManager.hasUserRestriction(DISALLOW_WIFI_TETHERING)).thenReturn(true);
 
         assertThat(WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mContext)).isFalse();
     }
 
     @Test
-    public void isWifiTetheringAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(
-            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
-        when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_TETHERING)).thenReturn(false);
+    public void isWifiTetheringAllowed_noDisallowRestriction_shouldReturnTrue() {
+        when(mUserManager.hasUserRestriction(DISALLOW_WIFI_TETHERING)).thenReturn(false);
 
         assertThat(WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(mContext)).isTrue();
     }
 
     @Test
-    public void isWifiDirectAllowed_setSDKForS_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
-        when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)).thenReturn(true);
-
-        assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue();
-    }
-
-    @Test
-    public void isWifiDirectAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() {
-        ReflectionHelpers.setStaticField(
-            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
-        when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)).thenReturn(true);
+    public void isWifiDirectAllowed_hasDisallowRestriction_shouldReturnFalse() {
+        when(mUserManager.hasUserRestriction(DISALLOW_WIFI_DIRECT)).thenReturn(true);
 
         assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isFalse();
     }
 
     @Test
-    public void isWifiDirectAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(
-            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
-        when(mBundle.getBoolean(UserManager.DISALLOW_WIFI_DIRECT)).thenReturn(false);
+    public void isWifiDirectAllowed_noDisallowRestriction_shouldReturnTrue() {
+        when(mUserManager.hasUserRestriction(DISALLOW_WIFI_DIRECT)).thenReturn(false);
 
         assertThat(WifiEnterpriseRestrictionUtils.isWifiDirectAllowed(mContext)).isTrue();
     }
 
     @Test
-    public void isAddWifiConfigAllowed_setSDKForS_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
-        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
-
-        assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
-    }
-
-    @Test
-    public void isAddWifiConfigAllowed_setSDKForTAndDisallowForRestriction_shouldReturnFalse() {
-        ReflectionHelpers.setStaticField(
-            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
-        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
+    public void isAddWifiConfigAllowed_hasDisallowRestriction_shouldReturnFalse() {
+        when(mUserManager.hasUserRestriction(DISALLOW_ADD_WIFI_CONFIG)).thenReturn(true);
 
         assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isFalse();
     }
 
     @Test
-    public void isAddWifiConfigAllowed_setSDKForTAndAllowForRestriction_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(
-            Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
-        when(mBundle.getBoolean(UserManager.DISALLOW_ADD_WIFI_CONFIG)).thenReturn(false);
+    public void isAddWifiConfigAllowed_noDisallowRestriction_shouldReturnTrue() {
+        when(mUserManager.hasUserRestriction(DISALLOW_ADD_WIFI_CONFIG)).thenReturn(false);
 
         assertThat(WifiEnterpriseRestrictionUtils.isAddWifiConfigAllowed(mContext)).isTrue();
     }
@@ -143,7 +112,7 @@
     }
 
     @Test
-    public void isChangeWifiStateAllowed_hasNoDisallowRestriction_shouldReturnTrue() {
+    public void isChangeWifiStateAllowed_noDisallowRestriction_shouldReturnTrue() {
         when(mUserManager.hasUserRestriction(DISALLOW_CHANGE_WIFI_STATE)).thenReturn(false);
 
         assertThat(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).isTrue();
@@ -151,7 +120,7 @@
 
     @Test
     public void hasUserRestrictionFromT_setSDKForS_shouldReturnTrue() {
-        ReflectionHelpers.setStaticField(Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.S);
+        ReflectionHelpers.setStaticField(Build.VERSION.class, SDK_INT, VERSION_CODES_S);
 
         assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key"))
                 .isFalse();
@@ -159,8 +128,7 @@
 
     @Test
     public void hasUserRestrictionFromT_setSDKForT_shouldReturnHasUserRestriction() {
-        ReflectionHelpers.setStaticField(
-                Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.TIRAMISU);
+        ReflectionHelpers.setStaticField(Build.VERSION.class, SDK_INT, VERSION_CODES_T);
         when(mUserManager.hasUserRestriction(anyString())).thenReturn(false);
 
         assertThat(WifiEnterpriseRestrictionUtils.hasUserRestrictionFromT(mContext, "key"))
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index f72a8dc..5b96159 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -198,20 +198,29 @@
                     android:clickable="false"
                     android:focusable="false">
 
-                    <FrameLayout
-                        android:layout_weight="1"
-                        android:orientation="vertical"
-                        android:clickable="false"
+                    <LinearLayout
                         android:layout_width="wrap_content"
-                        android:layout_height="match_parent">
+                        android:layout_height="match_parent"
+                        android:layout_weight="1"
+                        android:gravity="start|center_vertical"
+                        android:orientation="vertical"
+                        android:clickable="false">
                         <TextView
                             android:id="@+id/wifi_toggle_title"
                             android:text="@string/turn_on_wifi"
                             android:layout_width="wrap_content"
-                            android:layout_height="match_parent"
+                            android:layout_height="wrap_content"
                             android:gravity="start|center_vertical"
                             android:textAppearance="@style/TextAppearance.InternetDialog"/>
-                    </FrameLayout>
+                        <TextView
+                            android:id="@+id/wifi_toggle_summary"
+                            android:text="@string/wifitrackerlib_admin_restricted_network"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:gravity="start|center_vertical"
+                            android:textAppearance="@style/TextAppearance.InternetDialog.Secondary"
+                            android:visibility="gone"/>
+                    </LinearLayout>
 
                     <FrameLayout
                         android:layout_width="@dimen/settingslib_switch_track_width"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 80582ef..a0115e8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -561,6 +561,9 @@
     <!-- The height of the gap between adjacent notification sections. -->
     <dimen name="notification_section_divider_height">@dimen/notification_side_paddings</dimen>
 
+    <!-- The height of the gap between adjacent notification sections on lockscreen. -->
+    <dimen name="notification_section_divider_height_lockscreen">4dp</dimen>
+
     <!-- Size of the face pile shown on one-line (children of a group) conversation notifications -->
     <dimen name="conversation_single_line_face_pile_size">24dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt
new file mode 100644
index 0000000..f4f39a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+/**
+ * Interface for controlling the on finger down & on finger up events.
+ */
+interface AlternateUdfpsTouchProvider {
+
+    /**
+     * This operation is used to notify the Fingerprint HAL that
+     * a fingerprint has been detected on the device's screen.
+     *
+     * See fingerprint/ISession#onPointerDown for more details.
+     */
+    fun onPointerDown(pointerId: Long, x: Int, y: Int, minor: Float, major: Float)
+
+    /**
+     * onPointerUp:
+     *
+     * This operation can be invoked when the HAL is performing any one of: ISession#authenticate,
+     * ISession#enroll, ISession#detectInteraction. This operation is used to indicate
+     * that a fingerprint that was previously down, is now up.
+     *
+     * See fingerprint/ISession#onPointerUp for more details.
+     */
+    fun onPointerUp(pointerId: Long)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index eba87e4..2847246 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -133,6 +133,7 @@
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
     @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+    @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
 
     // Tracks the velocity of a touch to help filter out the touches that move too fast.
     @Nullable private VelocityTracker mVelocityTracker;
@@ -384,6 +385,7 @@
                     mActivePointerId = event.getPointerId(0);
                     mVelocityTracker.addMovement(event);
                     handled = true;
+                    mAcquiredReceived = false;
                 }
                 if ((withinSensorArea || fromUdfpsView) && shouldTryToDismissKeyguard()) {
                     Log.v(TAG, "onTouch | dismiss keyguard ACTION_DOWN");
@@ -537,7 +539,8 @@
             @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             @NonNull SystemUIDialogManager dialogManager,
             @NonNull LatencyTracker latencyTracker,
-            @NonNull ActivityLaunchAnimator activityLaunchAnimator) {
+            @NonNull ActivityLaunchAnimator activityLaunchAnimator,
+            @NonNull Optional<AlternateUdfpsTouchProvider> aternateTouchProvider) {
         mContext = context;
         mExecution = execution;
         mVibrator = vibrator;
@@ -566,6 +569,7 @@
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLatencyTracker = latencyTracker;
         mActivityLaunchAnimator = activityLaunchAnimator;
+        mAlternateTouchProvider = aternateTouchProvider.orElse(null);
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -782,6 +786,7 @@
 
     private void onFingerDown(long requestId, int x, int y, float minor, float major) {
         mExecution.assertIsMainThread();
+
         if (mOverlay == null) {
             Log.w(TAG, "Null request in onFingerDown");
             return;
@@ -806,7 +811,11 @@
             }
         }
         mOnFingerDown = true;
-        mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
+        if (mAlternateTouchProvider != null) {
+            mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
+        } else {
+            mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
+        }
         Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
 
         final UdfpsView view = mOverlay.getOverlayView();
@@ -827,7 +836,11 @@
         mActivePointerId = -1;
         mAcquiredReceived = false;
         if (mOnFingerDown) {
-            mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+            if (mAlternateTouchProvider != null) {
+                mAlternateTouchProvider.onPointerUp(requestId);
+            } else {
+                mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
+            }
             for (Callback cb : mCallbacks) {
                 cb.onFingerUp();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7b65f45..bbeb66c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,6 +29,7 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
 import com.android.systemui.biometrics.UdfpsHbmProvider;
 import com.android.systemui.biometrics.dagger.BiometricsModule;
 import com.android.systemui.classifier.FalsingModule;
@@ -182,6 +183,9 @@
     @BindsOptionalOf
     abstract UdfpsHbmProvider optionalUdfpsHbmProvider();
 
+    @BindsOptionalOf
+    abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
+
     @SysUISingleton
     @Binds
     abstract SystemClock bindSystemClock(SystemClockImpl systemClock);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index c147fde..54b9430 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -42,6 +42,7 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
+import java.util.Optional;
 import javax.inject.Inject;
 import javax.inject.Named;
 
@@ -77,7 +78,7 @@
 
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private float mCurrentExpansion;
-    private final CentralSurfaces mCentralSurfaces;
+    private final Optional<CentralSurfaces> mCentralSurfaces;
 
     private VelocityTracker mVelocityTracker;
 
@@ -107,7 +108,9 @@
                         // If the user scrolling favors a vertical direction, begin capturing
                         // scrolls.
                         mCapture = Math.abs(distanceY) > Math.abs(distanceX);
-                        mBouncerInitiallyShowing = mCentralSurfaces.isBouncerShowing();
+                        mBouncerInitiallyShowing = mCentralSurfaces
+                                .map(CentralSurfaces::isBouncerShowing)
+                                .orElse(false);
 
                         if (mCapture) {
                             // Since the user is dragging the bouncer up, set scrimmed to false.
@@ -129,13 +132,17 @@
                         return true;
                     }
 
+                    if (!mCentralSurfaces.isPresent()) {
+                        return true;
+                    }
+
                     // For consistency, we adopt the expansion definition found in the
                     // PanelViewController. In this case, expansion refers to the view above the
                     // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer
                     // is fully hidden at full expansion (1) and fully visible when fully collapsed
                     // (0).
                     final float screenTravelPercentage = Math.abs(e1.getY() - e2.getY())
-                            / mCentralSurfaces.getDisplayHeight();
+                            / mCentralSurfaces.get().getDisplayHeight();
                     setPanelExpansion(mBouncerInitiallyShowing
                             ? screenTravelPercentage : 1 - screenTravelPercentage);
                     return true;
@@ -171,7 +178,7 @@
     public BouncerSwipeTouchHandler(
             DisplayMetrics displayMetrics,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            CentralSurfaces centralSurfaces,
+            Optional<CentralSurfaces> centralSurfaces,
             NotificationShadeWindowController notificationShadeWindowController,
             ValueAnimatorCreator valueAnimatorCreator,
             VelocityTrackerFactory velocityTrackerFactory,
@@ -195,7 +202,7 @@
 
     @Override
     public void getTouchInitiationRegion(Region region) {
-        if (mCentralSurfaces.isBouncerShowing()) {
+        if (mCentralSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
             region.op(new Rect(0, 0, mDisplayMetrics.widthPixels,
                             Math.round(
                                     mDisplayMetrics.heightPixels * mBouncerZoneScreenPercentage)),
@@ -306,8 +313,12 @@
     }
 
     protected void flingToExpansion(float velocity, float expansion) {
+        if (!mCentralSurfaces.isPresent()) {
+            return;
+        }
+
         // The animation utils deal in pixel units, rather than expansion height.
-        final float viewHeight = mCentralSurfaces.getDisplayHeight();
+        final float viewHeight = mCentralSurfaces.get().getDisplayHeight();
         final float currentHeight = viewHeight * mCurrentExpansion;
         final float targetHeight = viewHeight * expansion;
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index c9d300b..60ef85d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -89,6 +89,7 @@
         holder.seekBar.thumb.alpha = if (data.seekAvailable) 255 else 0
         holder.seekBar.isEnabled = data.seekAvailable
         progressDrawable?.animate = data.playing && !data.scrubbing
+        progressDrawable?.transitionEnabled = !data.seekAvailable
 
         if (holder.seekBar.maxHeight != seekBarEnabledMaxHeight) {
             holder.seekBar.maxHeight = seekBarEnabledMaxHeight
diff --git a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
index fbd17d7..88f6f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
@@ -5,13 +5,15 @@
 import android.animation.ValueAnimator
 import android.content.res.ColorStateList
 import android.graphics.Canvas
-import android.graphics.Color
 import android.graphics.ColorFilter
 import android.graphics.Paint
 import android.graphics.Path
 import android.graphics.PixelFormat
 import android.graphics.drawable.Drawable
 import android.os.SystemClock
+import android.util.MathUtils.lerp
+import android.util.MathUtils.lerpInv
+import android.util.MathUtils.lerpInvSat
 import androidx.annotation.VisibleForTesting
 import com.android.internal.graphics.ColorUtils
 import com.android.systemui.animation.Interpolators
@@ -34,6 +36,13 @@
     private var phaseOffset = 0f
     private var lastFrameTime = -1L
 
+    /* distance over which amplitude drops to zero, measured in wavelengths */
+    private val transitionPeriods = 1.5f
+    /* wave endpoint as percentage of bar when play position is zero */
+    private val minWaveEndpoint = 0.2f
+    /* wave endpoint as percentage of bar when play position matches wave endpoint */
+    private val matchedWaveEndpoint = 0.6f
+
     // Horizontal length of the sine wave
     var waveLength = 0f
     // Height of each peak of the sine wave
@@ -51,6 +60,12 @@
             linePaint.strokeWidth = value
         }
 
+    var transitionEnabled = true
+        set(value) {
+            field = value
+            invalidateSelf()
+        }
+
     init {
         wavePaint.strokeCap = Paint.Cap.ROUND
         linePaint.strokeCap = Paint.Cap.ROUND
@@ -95,57 +110,90 @@
         if (animate) {
             invalidateSelf()
             val now = SystemClock.uptimeMillis()
-            phaseOffset -= (now - lastFrameTime) / 1000f * phaseSpeed
+            phaseOffset += (now - lastFrameTime) / 1000f * phaseSpeed
             phaseOffset %= waveLength
             lastFrameTime = now
         }
 
-        val totalProgressPx = (bounds.width() * (level / 10_000f))
+        val progress = level / 10_000f
+        val totalProgressPx = bounds.width() * progress
+        val waveProgressPx = bounds.width() * (
+            if (!transitionEnabled || progress > matchedWaveEndpoint) progress else
+            lerp(minWaveEndpoint, matchedWaveEndpoint, lerpInv(0f, matchedWaveEndpoint, progress)))
+
+        // Build Wiggly Path
+        val waveStart = -phaseOffset
+        val waveEnd = waveProgressPx
+        val transitionLength = if (transitionEnabled) transitionPeriods * waveLength else 0.01f
+
+        // helper function, computes amplitude for wave segment
+        val computeAmplitude: (Float, Float) -> Float = { x, sign ->
+            sign * heightFraction * lineAmplitude *
+                    lerpInvSat(waveEnd, waveEnd - transitionLength, x)
+        }
+
+        var currentX = waveEnd
+        var waveSign = if (phaseOffset < waveLength / 2) 1f else -1f
+        path.rewind()
+
+        // Draw flat line from end to wave endpoint
+        path.moveTo(bounds.width().toFloat(), 0f)
+        path.lineTo(waveEnd, 0f)
+
+        // First wave has shortened wavelength
+        // approx quarter wave gets us to first wave peak
+        // shouldn't be big enough to notice it's not a sin wave
+        currentX -= phaseOffset % (waveLength / 2)
+        val controlRatio = 0.25f
+        var currentAmp = computeAmplitude(currentX, waveSign)
+        path.cubicTo(
+            waveEnd, currentAmp * controlRatio,
+            lerp(currentX, waveEnd, controlRatio), currentAmp,
+            currentX, currentAmp)
+
+        // Other waves have full wavelength
+        val dist = -1 * waveLength / 2f
+        while (currentX > waveStart) {
+            waveSign = -waveSign
+            val nextX = currentX + dist
+            val midX = currentX + dist / 2
+            val nextAmp = computeAmplitude(nextX, waveSign)
+            path.cubicTo(
+                midX, currentAmp,
+                midX, nextAmp,
+                nextX, nextAmp)
+            currentAmp = nextAmp
+            currentX = nextX
+        }
+
+        // Draw path; clip to progress position
         canvas.save()
         canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
-        // Clip drawing, so we stop at the thumb
         canvas.clipRect(
                 0f,
                 -lineAmplitude - strokeWidth,
                 totalProgressPx,
                 lineAmplitude + strokeWidth)
-
-        // The squiggly line
-        val start = phaseOffset
-        var currentX = start
-        var waveSign = 1f
-        path.rewind()
-        path.moveTo(start, lineAmplitude * heightFraction)
-        while (currentX < totalProgressPx) {
-            val nextX = currentX + waveLength / 2f
-            val nextWaveSign = waveSign * -1
-            path.cubicTo(
-                    currentX + waveLength / 4f, lineAmplitude * waveSign * heightFraction,
-                    nextX - waveLength / 4f, lineAmplitude * nextWaveSign * heightFraction,
-                    nextX, lineAmplitude * nextWaveSign * heightFraction)
-            currentX = nextX
-            waveSign = nextWaveSign
-        }
-        wavePaint.style = Paint.Style.STROKE
         canvas.drawPath(path, wavePaint)
         canvas.restore()
 
+        // Draw path; clip between progression position & far edge
+        canvas.save()
+        canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
+        canvas.clipRect(
+                totalProgressPx,
+                -lineAmplitude - strokeWidth,
+                bounds.width().toFloat(),
+                lineAmplitude + strokeWidth)
+        canvas.drawPath(path, linePaint)
+        canvas.restore()
+
         // Draw round line cap at the beginning of the wave
-        val startAmp = cos(abs(phaseOffset) / waveLength * TWO_PI)
-        val p = Paint()
-        p.color = Color.WHITE
+        val startAmp = cos(abs(waveEnd - phaseOffset) / waveLength * TWO_PI)
         canvas.drawPoint(
                 bounds.left.toFloat(),
                 bounds.centerY() + startAmp * lineAmplitude * heightFraction,
                 wavePaint)
-
-        // Draw continuous line, to the right of the thumb
-        canvas.drawLine(
-                bounds.left.toFloat() + totalProgressPx,
-                bounds.centerY().toFloat(),
-                bounds.width().toFloat(),
-                bounds.centerY().toFloat(),
-                linePaint)
     }
 
     override fun getOpacity(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 8921e95..8ca095d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -58,6 +58,7 @@
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
@@ -136,6 +137,7 @@
     private Drawable mBackgroundOff = null;
     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private boolean mCanConfigMobileData;
+    private boolean mCanChangeWifiState;
 
     // Wi-Fi entries
     private int mWifiNetworkHeight;
@@ -180,6 +182,7 @@
         mWifiManager = mInternetDialogController.getWifiManager();
         mCanConfigMobileData = canConfigMobileData;
         mCanConfigWifi = canConfigWifi;
+        mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
         mKeyguard = keyguardStateController;
 
         mUiEventLogger = uiEventLogger;
@@ -449,6 +452,14 @@
         }
         mTurnWifiOnLayout.setBackground(
                 (isDeviceLocked && mConnectedWifiEntry != null) ? mBackgroundOn : null);
+
+        if (!mCanChangeWifiState && mWiFiToggle.isEnabled()) {
+            mWiFiToggle.setEnabled(false);
+            mWifiToggleTitleText.setEnabled(false);
+            final TextView summaryText = mDialogView.requireViewById(R.id.wifi_toggle_summary);
+            summaryText.setEnabled(false);
+            summaryText.setVisibility(View.VISIBLE);
+        }
     }
 
     @MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index f15df8e..c1ea6bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -72,6 +72,9 @@
     dumpManager: DumpManager
 ) : Dumpable {
     private var pulseHeight: Float = 0f
+    @get:VisibleForTesting
+    var fractionToShade: Float = 0f
+        private set
     private var useSplitShade: Boolean = false
     private lateinit var nsslController: NotificationStackScrollLayoutController
     lateinit var notificationPanelController: NotificationPanelViewController
@@ -405,9 +408,9 @@
             if (field != value || forceApplyAmount) {
                 field = value
                 if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
-                    val notificationShelfProgress =
+                    fractionToShade =
                         MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
-                    nsslController.setTransitionToFullShadeAmount(notificationShelfProgress)
+                    nsslController.setTransitionToFullShadeAmount(fractionToShade)
 
                     qSDragProgress = MathUtils.saturate(dragDownAmount / qsTransitionDistance)
                     qS.setTransitionToFullShadeAmount(field, qSDragProgress)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 633786f..afce945 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -85,9 +85,6 @@
     private NotificationShelfController mController;
     private float mActualWidth = -1;
 
-    /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
-    private float mFractionToShade;
-
     public NotificationShelf(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -234,13 +231,6 @@
     }
 
     /**
-     * @param fractionToShade Fraction of lockscreen to shade transition
-     */
-    public void setFractionToShade(float fractionToShade) {
-        mFractionToShade = fractionToShade;
-    }
-
-    /**
      * @return Actual width of shelf, accounting for possible ongoing width animation
      */
     public int getActualWidth() {
@@ -411,7 +401,8 @@
                 || !mShowNotificationShelf
                 || numViewsInShelf < 1f;
 
-        final float fractionToShade = Interpolators.STANDARD.getInterpolation(mFractionToShade);
+        final float fractionToShade = Interpolators.STANDARD.getInterpolation(
+                mAmbientState.getFractionToShade());
         final float shortestWidth = mShelfIcons.calculateWidthFor(numViewsInShelf);
         updateActualWidth(fractionToShade, shortestWidth);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index efb46b96..7245cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2658,9 +2658,6 @@
             return;
         }
 
-        // bail out if no public version
-        if (mPublicLayout.getChildCount() == 0) return;
-
         if (!animated) {
             mPublicLayout.animate().cancel();
             mPrivateLayout.animate().cancel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 25999a7..9acd60e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -82,6 +82,23 @@
     private boolean mAppearing;
     private float mPulseHeight = MAX_PULSE_HEIGHT;
 
+    /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
+    private float mFractionToShade;
+
+    /**
+     * @param fractionToShade Fraction of lockscreen to shade transition
+     */
+    public void setFractionToShade(float fractionToShade) {
+        mFractionToShade = fractionToShade;
+    }
+
+    /**
+     * @return fractionToShade Fraction of lockscreen to shade transition
+     */
+    public float getFractionToShade() {
+        return mFractionToShade;
+    }
+
     /** How we much we are sleeping. 1f fully dozing (AOD), 0f fully awake (for all other states) */
     private float mDozeAmount = 0.0f;
 
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 b52fd61..851794c 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
@@ -2295,7 +2295,7 @@
             int visibleIndex
     ) {
        return mStackScrollAlgorithm.getGapHeightForChild(mSectionsManager, visibleIndex, current,
-                previous);
+                previous, mAmbientState.getFractionToShade(), mAmbientState.isOnKeyguard());
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
@@ -5501,7 +5501,7 @@
      *                 where it remains until the next lockscreen-to-shade transition.
      */
     public void setFractionToShade(float fraction) {
-        mShelf.setFractionToShade(fraction);
+        mAmbientState.setFractionToShade(fraction);
         requestChildrenUpdate();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
index 9417aac..2b11f69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
@@ -19,9 +19,11 @@
 import android.content.res.Resources
 import android.util.Log
 import android.view.View.GONE
+import androidx.annotation.VisibleForTesting
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
 import com.android.systemui.statusbar.StatusBarState.KEYGUARD
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -41,6 +43,7 @@
 @Inject
 constructor(
     private val statusBarStateController: SysuiStatusBarStateController,
+    private val lockscreenShadeTransitionController: LockscreenShadeTransitionController,
     @Main private val resources: Resources
 ) {
 
@@ -129,7 +132,7 @@
         yield(dividerHeight + shelfIntrinsicHeight) // Only shelf.
 
         children.forEachIndexed { i, currentNotification ->
-            height += currentNotification.spaceNeeded(i, previous, stack, onLockscreen)
+            height += spaceNeeded(currentNotification, i, previous, stack, onLockscreen)
             previous = currentNotification
 
             val shelfHeight =
@@ -156,22 +159,28 @@
     private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView>
         get() = children.map { it as ExpandableView }
 
-    private fun onLockscreen() = statusBarStateController.state == KEYGUARD
+    @VisibleForTesting
+    fun onLockscreen() : Boolean {
+        return statusBarStateController.state == KEYGUARD
+                && lockscreenShadeTransitionController.fractionToShade == 0f
+    }
 
-    private fun ExpandableView.spaceNeeded(
+    @VisibleForTesting
+    fun spaceNeeded(
+        view: ExpandableView,
         visibleIndex: Int,
         previousView: ExpandableView?,
         stack: NotificationStackScrollLayout,
         onLockscreen: Boolean
     ): Float {
-        assert(isShowable(onLockscreen))
+        assert(view.isShowable(onLockscreen))
         var size =
             if (onLockscreen) {
-                getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
+                view.getMinHeight(/* ignoreTemporaryStates= */ true).toFloat()
             } else {
-                intrinsicHeight.toFloat()
+                view.intrinsicHeight.toFloat()
             }
-        size += calculateGapAndDividerHeight(stack, previousView, current = this, visibleIndex)
+        size += calculateGapAndDividerHeight(stack, previousView, current = view, visibleIndex)
         return size
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 9204c45e..22242b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -54,6 +54,7 @@
 
     private int mPaddingBetweenElements;
     private int mGapHeight;
+    private int mGapHeightOnLockscreen;
     private int mCollapsedSize;
 
     private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState();
@@ -87,6 +88,8 @@
         mPinnedZTranslationExtra = res.getDimensionPixelSize(
                 R.dimen.heads_up_pinned_elevation);
         mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
+        mGapHeightOnLockscreen = res.getDimensionPixelSize(
+                R.dimen.notification_section_divider_height_lockscreen);
         mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
         mMarginBottom = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
     }
@@ -305,7 +308,8 @@
                     ambientState.getSectionProvider(), i,
                     view, getPreviousView(i, state));
             if (applyGapHeight) {
-                currentY += mGapHeight;
+                currentY += getGapForLocation(
+                        ambientState.getFractionToShade(), ambientState.isOnKeyguard());
             }
 
             if (ambientState.getShelf() != null) {
@@ -454,8 +458,10 @@
                         ambientState.getSectionProvider(), i,
                         view, getPreviousView(i, algorithmState));
         if (applyGapHeight) {
-            algorithmState.mCurrentYPosition += expansionFraction * mGapHeight;
-            algorithmState.mCurrentExpandedYPosition += mGapHeight;
+            final float gap = getGapForLocation(
+                    ambientState.getFractionToShade(), ambientState.isOnKeyguard());
+            algorithmState.mCurrentYPosition += expansionFraction * gap;
+            algorithmState.mCurrentExpandedYPosition += gap;
         }
 
         viewState.yTranslation = algorithmState.mCurrentYPosition;
@@ -539,16 +545,29 @@
             SectionProvider sectionProvider,
             int visibleIndex,
             View child,
-            View previousChild) {
+            View previousChild,
+            float fractionToShade,
+            boolean onKeyguard) {
 
         if (childNeedsGapHeight(sectionProvider, visibleIndex, child,
                 previousChild)) {
-            return mGapHeight;
+            return getGapForLocation(fractionToShade, onKeyguard);
         } else {
             return 0;
         }
     }
 
+    @VisibleForTesting
+    float getGapForLocation(float fractionToShade, boolean onKeyguard) {
+        if (fractionToShade > 0f) {
+            return MathUtils.lerp(mGapHeightOnLockscreen, mGapHeight, fractionToShade);
+        }
+        if (onKeyguard) {
+            return mGapHeightOnLockscreen;
+        }
+        return mGapHeight;
+    }
+
     /**
      * Does a given child need a gap, i.e spacing before a view?
      *
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
index 6fdce1a..640adcc 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
@@ -22,6 +22,12 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.SystemUIBinder;
 import com.android.systemui.dagger.SystemUIModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+
+import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.recents.RecentsModule;
 
 import dagger.Subcomponent;
 
@@ -30,13 +36,17 @@
  */
 @SysUISingleton
 @Subcomponent(modules = {
+        CentralSurfacesDependenciesModule.class,
         DefaultComponentBinder.class,
         DependencyProvider.class,
-        SystemUIBinder.class,
+        KeyguardModule.class,
+        NotificationRowModule.class,
+        NotificationsModule.class,
+        RecentsModule.class,
         SystemUIModule.class,
+        TvSystemUIBinder.class,
         TVSystemUICoreStartableModule.class,
-        TvSystemUIModule.class,
-        TvSystemUIBinder.class})
+        TvSystemUIModule.class})
 public interface TvSysUIComponent extends SysUIComponent {
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
index 9b2702f..1243c47 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletView.java
@@ -215,6 +215,7 @@
         logoView.setImageDrawable(mContext.getDrawable(R.drawable.ic_qs_plus));
         mEmptyStateView.<TextView>requireViewById(R.id.empty_state_title).setText(label);
         mEmptyStateView.setOnClickListener(clickListener);
+        mAppButton.setOnClickListener(clickListener);
     }
 
     void showErrorMessage(@Nullable CharSequence message) {
@@ -256,6 +257,11 @@
     }
 
     @VisibleForTesting
+    Button getAppButton() {
+        return mAppButton;
+    }
+
+    @VisibleForTesting
     TextView getErrorView() {
         return mErrorView;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 05cf6a50..5d624cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -22,6 +22,7 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
@@ -183,6 +184,8 @@
     private SystemUIDialogManager mSystemUIDialogManager;
     @Mock
     private ActivityLaunchAnimator mActivityLaunchAnimator;
+    @Mock
+    private AlternateUdfpsTouchProvider mTouchProvider;
 
     // Capture listeners so that they can be used to send events
     @Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -256,7 +259,8 @@
                 mUnlockedScreenOffAnimationController,
                 mSystemUIDialogManager,
                 mLatencyTracker,
-                mActivityLaunchAnimator);
+                mActivityLaunchAnimator,
+                Optional.of(mTouchProvider));
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
         verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -418,9 +422,10 @@
         mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
         moveEvent.recycle();
         // THEN FingerprintManager is notified about onPointerDown
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(mUdfpsController.mSensorProps.sensorId),
+        verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
                 eq(0), eq(0), eq(0f), eq(0f));
+        verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
+                anyFloat(), anyFloat());
         verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
         // AND illumination begins
         verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
@@ -447,9 +452,10 @@
         // AND onIlluminatedRunnable that notifies FingerprintManager is set
         verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
         mOnIlluminatedRunnableCaptor.getValue().run();
-        verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID),
-                eq(mUdfpsController.mSensorProps.sensorId),
+        verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
                 eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
+        verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
+                anyFloat(), anyFloat());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index e175af7..d0b3d6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -57,6 +57,8 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@@ -110,7 +112,7 @@
         mTouchHandler = new BouncerSwipeTouchHandler(
                 mDisplayMetrics,
                 mStatusBarKeyguardViewManager,
-                mCentralSurfaces,
+                Optional.of(mCentralSurfaces),
                 mNotificationShadeWindowController,
                 mValueAnimatorCreator,
                 mVelocityTrackerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt
index e3cd90b..d087b0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SquigglyProgressTest.kt
@@ -19,7 +19,7 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
-import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
@@ -35,8 +35,7 @@
 
     lateinit var squigglyProgress: SquigglyProgress
     @Mock lateinit var canvas: Canvas
-    @Captor lateinit var wavePaintCaptor: ArgumentCaptor<Paint>
-    @Captor lateinit var linePaintCaptor: ArgumentCaptor<Paint>
+    @Captor lateinit var paintCaptor: ArgumentCaptor<Paint>
     @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
 
     @Before
@@ -53,9 +52,7 @@
     fun testDrawPathAndLine() {
         squigglyProgress.draw(canvas)
 
-        verify(canvas).drawPath(any(), wavePaintCaptor.capture())
-        verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
-                linePaintCaptor.capture())
+        verify(canvas, times(2)).drawPath(any(), paintCaptor.capture())
     }
 
     @Test
@@ -69,12 +66,11 @@
     fun testStrokeWidth() {
         squigglyProgress.draw(canvas)
 
-        verify(canvas).drawPath(any(), wavePaintCaptor.capture())
-        verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
-                linePaintCaptor.capture())
+        verify(canvas, times(2)).drawPath(any(), paintCaptor.capture())
+        val (wavePaint, linePaint) = paintCaptor.getAllValues()
 
-        assertThat(wavePaintCaptor.value.strokeWidth).isEqualTo(strokeWidth)
-        assertThat(linePaintCaptor.value.strokeWidth).isEqualTo(strokeWidth)
+        assertThat(wavePaint.strokeWidth).isEqualTo(strokeWidth)
+        assertThat(linePaint.strokeWidth).isEqualTo(strokeWidth)
     }
 
     @Test
@@ -82,13 +78,12 @@
         squigglyProgress.alpha = alpha
         squigglyProgress.draw(canvas)
 
-        verify(canvas).drawPath(any(), wavePaintCaptor.capture())
-        verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
-                linePaintCaptor.capture())
+        verify(canvas, times(2)).drawPath(any(), paintCaptor.capture())
+        val (wavePaint, linePaint) = paintCaptor.getAllValues()
 
         assertThat(squigglyProgress.alpha).isEqualTo(alpha)
-        assertThat(wavePaintCaptor.value.alpha).isEqualTo(alpha)
-        assertThat(linePaintCaptor.value.alpha).isEqualTo((alpha / 255f * DISABLED_ALPHA).toInt())
+        assertThat(wavePaint.alpha).isEqualTo(alpha)
+        assertThat(linePaint.alpha).isEqualTo((alpha / 255f * DISABLED_ALPHA).toInt())
     }
 
     @Test
@@ -96,12 +91,11 @@
         squigglyProgress.colorFilter = colorFilter
         squigglyProgress.draw(canvas)
 
-        verify(canvas).drawPath(any(), wavePaintCaptor.capture())
-        verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
-                linePaintCaptor.capture())
+        verify(canvas, times(2)).drawPath(any(), paintCaptor.capture())
+        val (wavePaint, linePaint) = paintCaptor.getAllValues()
 
-        assertThat(wavePaintCaptor.value.colorFilter).isEqualTo(colorFilter)
-        assertThat(linePaintCaptor.value.colorFilter).isEqualTo(colorFilter)
+        assertThat(wavePaint.colorFilter).isEqualTo(colorFilter)
+        assertThat(linePaint.colorFilter).isEqualTo(colorFilter)
     }
 
     @Test
@@ -109,12 +103,11 @@
         squigglyProgress.setTint(tint)
         squigglyProgress.draw(canvas)
 
-        verify(canvas).drawPath(any(), wavePaintCaptor.capture())
-        verify(canvas).drawLine(anyFloat(), anyFloat(), anyFloat(), anyFloat(),
-                linePaintCaptor.capture())
+        verify(canvas, times(2)).drawPath(any(), paintCaptor.capture())
+        val (wavePaint, linePaint) = paintCaptor.getAllValues()
 
-        assertThat(wavePaintCaptor.value.color).isEqualTo(tint)
-        assertThat(linePaintCaptor.value.color).isEqualTo(
+        assertThat(wavePaint.color).isEqualTo(tint)
+        assertThat(linePaint.color).isEqualTo(
                 ColorUtils.setAlphaComponent(tint, DISABLED_ALPHA))
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index cf97bda..616f894 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -21,12 +21,15 @@
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.Switch;
 import android.widget.TextView;
 
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.test.filters.SmallTest;
 
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.logging.UiEventLogger;
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -42,6 +45,7 @@
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
 
 import java.util.List;
 
@@ -79,12 +83,16 @@
     private LinearLayout mEthernet;
     private LinearLayout mMobileDataToggle;
     private LinearLayout mWifiToggle;
+    private Switch mWifiToggleSwitch;
+    private TextView mWifiToggleSummary;
     private LinearLayout mConnectedWifi;
     private RecyclerView mWifiList;
     private LinearLayout mSeeAll;
     private LinearLayout mWifiScanNotify;
     private TextView mAirplaneModeSummaryText;
 
+    private MockitoSession mMockitoSession;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -101,6 +109,15 @@
                 .thenReturn(MOBILE_NETWORK_SUMMARY);
         when(mInternetDialogController.getWifiManager()).thenReturn(mWifiManager);
 
+        mMockitoSession = ExtendedMockito.mockitoSession()
+                .spyStatic(WifiEnterpriseRestrictionUtils.class)
+                .startMocking();
+        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
+
+        createInternetDialog();
+    }
+
+    private void createInternetDialog() {
         mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
                 mInternetDialogController, true, true, true, mock(UiEventLogger.class), mHandler,
                 mBgExecutor, mKeyguard);
@@ -114,6 +131,8 @@
         mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
         mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_network_layout);
         mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+        mWifiToggleSwitch = mDialogView.requireViewById(R.id.wifi_toggle);
+        mWifiToggleSummary = mDialogView.requireViewById(R.id.wifi_toggle_summary);
         mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
         mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
         mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
@@ -124,6 +143,7 @@
     @After
     public void tearDown() {
         mInternetDialog.dismissDialog();
+        mMockitoSession.finishMocking();
     }
 
     @Test
@@ -411,6 +431,33 @@
     }
 
     @Test
+    public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
+        mInternetDialog.dismissDialog();
+        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
+        createInternetDialog();
+
+        mInternetDialog.updateDialog(false);
+
+        // Disable Wi-Fi switch and show restriction message in summary.
+        assertThat(mWifiToggleSwitch.isEnabled()).isFalse();
+        assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mWifiToggleSummary.getText().length()).isNotEqualTo(0);
+    }
+
+    @Test
+    public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
+        mInternetDialog.dismissDialog();
+        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
+        createInternetDialog();
+
+        mInternetDialog.updateDialog(false);
+
+        // Enable Wi-Fi switch and hide restriction message in summary.
+        assertThat(mWifiToggleSwitch.isEnabled()).isTrue();
+        assertThat(mWifiToggleSummary.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
     public void updateDialog_wifiOn_hideWifiScanNotify() {
         // The preconditions WiFi ON and WiFi entries are already in setUp()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 5d16036..4270d72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -144,7 +144,7 @@
     }
 
     private fun setFractionToShade(fraction: Float) {
-        shelf.setFractionToShade(fraction)
+        whenever(ambientState.fractionToShade).thenReturn(fraction)
     }
 
     private fun setOnLockscreen(isOnLockscreen: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
index 968e16a..c3658ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
@@ -23,6 +23,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.SysuiStatusBarStateController
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -44,7 +46,7 @@
 class NotificationStackSizeCalculatorTest : SysuiTestCase() {
 
     @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
-
+    @Mock private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
     @Mock private lateinit var stackLayout: NotificationStackScrollLayout
 
     private val testableResources = mContext.getOrCreateTestableResources()
@@ -63,6 +65,7 @@
         sizeCalculator =
             NotificationStackSizeCalculator(
                 statusBarStateController = sysuiStatusBarStateController,
+                lockscreenShadeTransitionController = lockscreenShadeTransitionController,
                 testableResources.resources)
     }
 
@@ -155,6 +158,55 @@
         assertThat(height).isAtMost(availableSpace)
     }
 
+    @Test
+    fun onLockscreen_onKeyguard_AndNotGoingToShade_returnsTrue() {
+        whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+        whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(0f)
+        assertThat(sizeCalculator.onLockscreen()).isTrue()
+    }
+
+    @Test
+    fun onLockscreen_goingToShade_returnsFalse() {
+        whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+        whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(0.5f)
+        assertThat(sizeCalculator.onLockscreen()).isFalse()
+    }
+
+    @Test
+    fun onLockscreen_notOnLockscreen_returnsFalse() {
+        whenever(sysuiStatusBarStateController.state).thenReturn(StatusBarState.SHADE)
+        whenever(lockscreenShadeTransitionController.fractionToShade).thenReturn(1f)
+        assertThat(sizeCalculator.onLockscreen()).isFalse()
+    }
+
+    @Test
+    fun spaceNeeded_onLockscreen_usesMinHeight() {
+        setGapHeight(0f)
+        // No divider height since we're testing one element where index = 0
+
+        val expandableView = createMockRow(rowHeight)
+        whenever(expandableView.getMinHeight(any())).thenReturn(5)
+        whenever(expandableView.intrinsicHeight).thenReturn(10)
+
+        val space = sizeCalculator.spaceNeeded(expandableView, visibleIndex = 0,
+                previousView = null, stack = stackLayout, onLockscreen = true)
+        assertThat(space).isEqualTo(5)
+    }
+
+    @Test
+    fun spaceNeeded_notOnLockscreen_usesIntrinsicHeight() {
+        setGapHeight(0f)
+        // No divider height since we're testing one element where index = 0
+
+        val expandableView = createMockRow(rowHeight)
+        whenever(expandableView.getMinHeight(any())).thenReturn(5)
+        whenever(expandableView.intrinsicHeight).thenReturn(10)
+
+        val space = sizeCalculator.spaceNeeded(expandableView, visibleIndex = 0,
+                previousView = null, stack = stackLayout, onLockscreen = false)
+        assertThat(space).isEqualTo(10)
+    }
+
     private fun computeMaxKeyguardNotifications(
         rows: List<ExpandableView>,
         availableSpace: Float,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 6b4dc58..1f90d0c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.statusbar.notification.stack
 
+import android.annotation.DimenRes
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.systemui.R
@@ -31,6 +32,14 @@
         mStatusBarKeyguardViewManager
     )
 
+    private val testableResources = mContext.orCreateTestableResources
+
+    private fun px(@DimenRes id: Int): Float =
+            testableResources.resources.getDimensionPixelSize(id).toFloat()
+
+    private val bigGap = px(R.dimen.notification_section_divider_height)
+    private val smallGap = px(R.dimen.notification_section_divider_height_lockscreen)
+
     @Before
     fun setUp() {
         whenever(notificationRow.viewState).thenReturn(expandableViewState)
@@ -80,4 +89,25 @@
         val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
         assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
     }
+
+    @Test
+    fun getGapForLocation_onLockscreen_returnsSmallGap() {
+        val gap = stackScrollAlgorithm.getGapForLocation(
+                /* fractionToShade= */ 0f, /* onKeyguard= */ true)
+        assertThat(gap).isEqualTo(smallGap)
+    }
+
+    @Test
+    fun getGapForLocation_goingToShade_interpolatesGap() {
+        val gap = stackScrollAlgorithm.getGapForLocation(
+                /* fractionToShade= */ 0.5f, /* onKeyguard= */ true)
+        assertThat(gap).isEqualTo(smallGap * 0.5f + bigGap * 0.5f)
+    }
+
+    @Test
+    fun getGapForLocation_notOnLockscreen_returnsBigGap() {
+        val gap = stackScrollAlgorithm.getGapForLocation(
+                /* fractionToShade= */ 0f, /* onKeyguard= */ false)
+        assertThat(gap).isEqualTo(bigGap)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index 01769e5..b1950ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -20,6 +20,7 @@
 import static android.view.View.VISIBLE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -323,6 +324,7 @@
         assertEquals(GONE, mWalletView.getCardCarousel().getVisibility());
         assertEquals(VISIBLE, mWalletView.getEmptyStateView().getVisibility());
         assertEquals(GONE, mWalletView.getErrorView().getVisibility());
+        assertTrue(mWalletView.getAppButton().hasOnClickListeners());
     }
 
     @Test
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 35f7e06..7cee203 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15093,7 +15093,7 @@
     @GuardedBy("this")
     final boolean canGcNowLocked() {
         for (BroadcastQueue q : mBroadcastQueues) {
-            if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isEmpty()) {
+            if (!q.mParallelBroadcasts.isEmpty() || !q.mDispatcher.isIdle()) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 90201a0..8c42d4d 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -266,22 +266,16 @@
                 }
                 FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO,
                         uid,
-                        FrameworkStatsLog
-                                .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNKNOWN,
-                        FrameworkStatsLog
-                                .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_UNKNOWN,
-                        FrameworkStatsLog
-                                .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__UNKNOWN_TRACKER,
+                        AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN, // RestrictionLevel
+                        AppBackgroundRestrictionsInfo.THRESHOLD_UNKNOWN,
+                        AppBackgroundRestrictionsInfo.UNKNOWN_TRACKER,
                         null /*byte[] fgs_tracker_info*/,
                         getBatteryTrackerInfoProtoLocked(uid) /*byte[] battery_tracker_info*/,
                         null /*byte[] broadcast_events_tracker_info*/,
                         null /*byte[] bind_service_events_tracker_info*/,
-                        FrameworkStatsLog
-                                .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_UNKNOWN,
-                        FrameworkStatsLog
-                                .APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__UNKNOWN,
-                        FrameworkStatsLog
-                                .APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN,
+                        AppBackgroundRestrictionsInfo.REASON_UNKNOWN, // ExemptionReason
+                        AppBackgroundRestrictionsInfo.UNKNOWN, // OptimizationLevel
+                        AppBackgroundRestrictionsInfo.SDK_UNKNOWN, // TargetSdk
                         isLowRamDeviceStatic());
             }
         }
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index bdda0b7..d70404f 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -116,6 +116,7 @@
 import android.database.ContentObserver;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.AppBackgroundRestrictionsInfo;
 import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
@@ -123,7 +124,6 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
-import android.os.PowerExemptionManager.ExemptionReason;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -1095,6 +1095,14 @@
                 DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "restriction_exempted_packages";
 
         /**
+         * Whether or not to show the notification for abusive apps, i.e. when the system
+         * detects it's draining significant amount of battery in the background.
+         * {@code true} - we'll show the prompt to user, {@code false} - we'll not show it.
+         */
+        static final String KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED =
+                DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "prompt_abusive_apps_to_bg_restricted";
+
+        /**
          * Default value to {@link #mBgAutoRestrictedBucket}.
          */
         static final boolean DEFAULT_BG_AUTO_RESTRICTED_BUCKET_ON_BG_RESTRICTION = false;
@@ -1119,6 +1127,11 @@
          */
         final boolean mDefaultBgPromptFgsWithNotiToBgRestricted;
 
+        /**
+         * Default value to {@link #mBgPromptAbusiveAppsToBgRestricted}.
+         */
+        final boolean mDefaultBgPromptAbusiveAppToBgRestricted;
+
         volatile boolean mBgAutoRestrictedBucket;
 
         volatile boolean mRestrictedBucketEnabled;
@@ -1144,10 +1157,17 @@
          */
         volatile boolean mBgPromptFgsWithNotiOnLongRunning;
 
+        /**
+         * @see #KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED.
+         */
+        volatile boolean mBgPromptAbusiveAppsToBgRestricted;
+
         ConstantsObserver(Handler handler, Context context) {
             super(handler);
             mDefaultBgPromptFgsWithNotiToBgRestricted = context.getResources().getBoolean(
                     com.android.internal.R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted);
+            mDefaultBgPromptAbusiveAppToBgRestricted = context.getResources().getBoolean(
+                    com.android.internal.R.bool.config_bg_prompt_abusive_apps_to_bg_restricted);
         }
 
         @Override
@@ -1172,6 +1192,9 @@
                     case KEY_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING:
                         updateBgPromptFgsWithNotiOnLongRunning();
                         break;
+                    case KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED:
+                        updateBgPromptAbusiveAppToBgRestricted();
+                        break;
                     case KEY_BG_RESTRICTION_EXEMPTED_PACKAGES:
                         updateBgRestrictionExemptedPackages();
                         break;
@@ -1209,6 +1232,7 @@
             updateBgLongFgsNotificationMinimalInterval();
             updateBgPromptFgsWithNotiToBgRestricted();
             updateBgPromptFgsWithNotiOnLongRunning();
+            updateBgPromptAbusiveAppToBgRestricted();
             updateBgRestrictionExemptedPackages();
         }
 
@@ -1251,6 +1275,13 @@
                     DEFAULT_BG_PROMPT_FGS_WITH_NOTIFICATION_ON_LONG_RUNNING);
         }
 
+        private void updateBgPromptAbusiveAppToBgRestricted() {
+            mBgPromptAbusiveAppsToBgRestricted = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED,
+                    mDefaultBgPromptAbusiveAppToBgRestricted);
+        }
+
         private void updateBgRestrictionExemptedPackages() {
             final String settings = DeviceConfig.getString(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1290,6 +1321,10 @@
             pw.print('=');
             pw.println(mBgPromptFgsWithNotiToBgRestricted);
             pw.print(prefix);
+            pw.print(KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED);
+            pw.print('=');
+            pw.println(mBgPromptAbusiveAppsToBgRestricted);
+            pw.print(prefix);
             pw.print(KEY_BG_RESTRICTION_EXEMPTED_PACKAGES);
             pw.print('=');
             pw.println(mBgRestrictionExemptedPackages.toString());
@@ -1881,76 +1916,59 @@
     private int getRestrictionLevelStatsd(@RestrictionLevel int level) {
         switch (level) {
             case RESTRICTION_LEVEL_UNKNOWN:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNKNOWN;
+                return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
             case RESTRICTION_LEVEL_UNRESTRICTED:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNRESTRICTED;
+                return AppBackgroundRestrictionsInfo.LEVEL_UNRESTRICTED;
             case RESTRICTION_LEVEL_EXEMPTED:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_EXEMPTED;
+                return AppBackgroundRestrictionsInfo.LEVEL_EXEMPTED;
             case RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_ADAPTIVE_BUCKET;
+                return AppBackgroundRestrictionsInfo.LEVEL_ADAPTIVE_BUCKET;
             case RESTRICTION_LEVEL_RESTRICTED_BUCKET:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_RESTRICTED_BUCKET;
+                return AppBackgroundRestrictionsInfo.LEVEL_RESTRICTED_BUCKET;
             case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_BACKGROUND_RESTRICTED;
+                return AppBackgroundRestrictionsInfo.LEVEL_BACKGROUND_RESTRICTED;
             case RESTRICTION_LEVEL_HIBERNATION:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_HIBERNATION;
+                return AppBackgroundRestrictionsInfo.LEVEL_HIBERNATION;
             default:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNKNOWN;
+                return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
         }
     }
 
     private int getThresholdStatsd(int reason) {
         switch (reason) {
             case REASON_MAIN_FORCED_BY_SYSTEM:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_RESTRICTED;
+                return AppBackgroundRestrictionsInfo.THRESHOLD_RESTRICTED;
             case REASON_MAIN_FORCED_BY_USER:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_USER;
+                return AppBackgroundRestrictionsInfo.THRESHOLD_USER;
             default:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_UNKNOWN;
+                return AppBackgroundRestrictionsInfo.THRESHOLD_UNKNOWN;
         }
     }
 
     private int getTrackerTypeStatsd(@TrackerType int type) {
         switch (type) {
             case TRACKER_TYPE_BATTERY:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BATTERY_TRACKER;
+                return AppBackgroundRestrictionsInfo.BATTERY_TRACKER;
             case TRACKER_TYPE_BATTERY_EXEMPTION:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BATTERY_EXEMPTION_TRACKER;
+                return AppBackgroundRestrictionsInfo.BATTERY_EXEMPTION_TRACKER;
             case TRACKER_TYPE_FGS:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__FGS_TRACKER;
+                return AppBackgroundRestrictionsInfo.FGS_TRACKER;
             case TRACKER_TYPE_MEDIA_SESSION:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__MEDIA_SESSION_TRACKER;
+                return AppBackgroundRestrictionsInfo.MEDIA_SESSION_TRACKER;
             case TRACKER_TYPE_PERMISSION:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__PERMISSION_TRACKER;
+                return AppBackgroundRestrictionsInfo.PERMISSION_TRACKER;
             case TRACKER_TYPE_BROADCAST_EVENTS:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BROADCAST_EVENTS_TRACKER;
+                return AppBackgroundRestrictionsInfo.BROADCAST_EVENTS_TRACKER;
             case TRACKER_TYPE_BIND_SERVICE_EVENTS:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BIND_SERVICE_EVENTS_TRACKER;
+                return AppBackgroundRestrictionsInfo.BIND_SERVICE_EVENTS_TRACKER;
             default:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__UNKNOWN_TRACKER;
+                return AppBackgroundRestrictionsInfo.UNKNOWN_TRACKER;
         }
     }
 
-    private @ExemptionReason int getExemptionReasonStatsd(int uid, @RestrictionLevel int level) {
+    private int getExemptionReasonStatsd(int uid, @RestrictionLevel int level) {
         if (level != RESTRICTION_LEVEL_EXEMPTED) {
-            return FrameworkStatsLog
-                    .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DENIED;
+            return AppBackgroundRestrictionsInfo.REASON_DENIED;
         }
 
         @ReasonCode final int reasonCode = getBackgroundRestrictionExemptionReason(uid);
@@ -1960,16 +1978,15 @@
     private int getOptimizationLevelStatsd(@RestrictionLevel int level) {
         switch (level) {
             case RESTRICTION_LEVEL_UNKNOWN:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__UNKNOWN;
+                return AppBackgroundRestrictionsInfo.UNKNOWN;
             case RESTRICTION_LEVEL_UNRESTRICTED:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__NOT_OPTIMIZED;
+                return AppBackgroundRestrictionsInfo.NOT_OPTIMIZED;
             case RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__OPTIMIZED;
+                return AppBackgroundRestrictionsInfo.OPTIMIZED;
             case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
-                return FrameworkStatsLog
-                        .APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__BACKGROUND_RESTRICTED;
+                return AppBackgroundRestrictionsInfo.BACKGROUND_RESTRICTED;
             default:
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__UNKNOWN;
+                return AppBackgroundRestrictionsInfo.UNKNOWN;
         }
     }
 
@@ -1977,26 +1994,26 @@
     private int getTargetSdkStatsd(String packageName) {
         final PackageManager pm = mInjector.getPackageManager();
         if (pm == null) {
-            return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+            return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
         }
         try {
             final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
             if (pkg == null || pkg.applicationInfo == null) {
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+                return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
             }
             final int targetSdk = pkg.applicationInfo.targetSdkVersion;
             if (targetSdk < Build.VERSION_CODES.S) {
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_PRE_S;
+                return AppBackgroundRestrictionsInfo.SDK_PRE_S;
             } else if (targetSdk < Build.VERSION_CODES.TIRAMISU) {
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_S;
+                return AppBackgroundRestrictionsInfo.SDK_S;
             } else if (targetSdk == Build.VERSION_CODES.TIRAMISU) {
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_T;
+                return AppBackgroundRestrictionsInfo.SDK_T;
             } else {
-                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+                return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
             }
         } catch (PackageManager.NameNotFoundException ignored) {
         }
-        return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+        return AppBackgroundRestrictionsInfo.SDK_UNKNOWN;
     }
 
     private void applyRestrictionLevel(String pkgName, int uid,
@@ -2296,6 +2313,13 @@
         }
 
         void postRequestBgRestrictedIfNecessary(String packageName, int uid) {
+            if (!mBgController.mConstantsObserver.mBgPromptAbusiveAppsToBgRestricted) {
+                if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+                    Slog.i(TAG, "Not requesting bg-restriction due to config");
+                }
+                return;
+            }
+
             final Intent intent = new Intent(Settings.ACTION_VIEW_ADVANCED_POWER_USAGE_DETAIL);
             intent.setData(Uri.fromParts(PACKAGE_SCHEME, packageName, null));
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index 872531a..49477ad 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -512,12 +512,24 @@
      */
     public boolean isEmpty() {
         synchronized (mLock) {
+            return isIdle()
+                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED) == 0
+                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED) == 0;
+        }
+    }
+
+    /**
+     * Have less check than {@link #isEmpty()}.
+     * The dispatcher is considered as idle even with deferred LOCKED_BOOT_COMPLETED/BOOT_COMPLETED
+     * broadcasts because those can be deferred until the first time the uid's process is started.
+     * @return
+     */
+    public boolean isIdle() {
+        synchronized (mLock) {
             return mCurrentBroadcast == null
                     && mOrderedBroadcasts.isEmpty()
                     && isDeferralsListEmpty(mDeferredBroadcasts)
-                    && isDeferralsListEmpty(mAlarmBroadcasts)
-                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_LOCKED_BOOT_COMPLETED) == 0
-                    && getBootCompletedBroadcastsUidsSize(Intent.ACTION_BOOT_COMPLETED) == 0;
+                    && isDeferralsListEmpty(mAlarmBroadcasts);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8a7fece..3ba99a8 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -2218,7 +2218,7 @@
     }
 
     boolean isIdle() {
-        return mParallelBroadcasts.isEmpty() && mDispatcher.isEmpty()
+        return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
                 && (mPendingBroadcast == null);
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 849f530..349174d 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -207,6 +207,9 @@
         STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
         STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
         STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO);
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO);
+        STORAGE_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES);
     }
 
     private static final Set<String> NEARBY_DEVICES_PERMISSIONS = new ArraySet<>();
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 9aa53f1..7baec62 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -79,7 +79,8 @@
 import java.util.function.Function;
 
 /** Resolves all Android component types [activities, services, providers and receivers]. */
-public class ComponentResolver extends ComponentResolverLocked implements Snappable {
+public class ComponentResolver extends ComponentResolverLocked implements
+        Snappable<ComponentResolverApi> {
     private static final boolean DEBUG = false;
     private static final String TAG = "PackageManager";
     private static final boolean DEBUG_FILTERS = false;
@@ -166,11 +167,13 @@
         mProvidersByAuthority = new ArrayMap<>();
         mDeferProtectedFilters = true;
 
-        mSnapshot = new SnapshotCache<ComponentResolverApi>(this, this) {
+        mSnapshot = new SnapshotCache<>(this, this) {
                 @Override
                 public ComponentResolverApi createSnapshot() {
-                    return new ComponentResolverSnapshot(ComponentResolver.this,
-                            userNeedsBadgingCache);
+                    synchronized (mLock) {
+                        return new ComponentResolverSnapshot(ComponentResolver.this,
+                                userNeedsBadgingCache);
+                    }
                 }};
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index eaf82b6..2d7d705 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -138,8 +138,8 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.policy.ForceShowNavigationBarSettingsObserver;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
+import com.android.internal.policy.KidsModeSettingsObserver;
 import com.android.internal.policy.ScreenDecorationsUtils;
 import com.android.internal.policy.SystemBarUtils;
 import com.android.internal.protolog.common.ProtoLog;
@@ -378,7 +378,7 @@
 
     private final WindowManagerInternal.AppTransitionListener mAppTransitionListener;
 
-    private final ForceShowNavigationBarSettingsObserver mForceShowNavigationBarSettingsObserver;
+    private final KidsModeSettingsObserver mKidsModeSettingsObserver;
     private boolean mForceShowNavigationBarEnabled;
 
     private class PolicyHandler extends Handler {
@@ -653,17 +653,17 @@
         });
         mHandler.post(mGestureNavigationSettingsObserver::register);
 
-        mForceShowNavigationBarSettingsObserver = new ForceShowNavigationBarSettingsObserver(
+        mKidsModeSettingsObserver = new KidsModeSettingsObserver(
                 mHandler, mContext);
-        mForceShowNavigationBarSettingsObserver.setOnChangeRunnable(() -> {
+        mKidsModeSettingsObserver.setOnChangeRunnable(() -> {
             synchronized (mLock) {
                 mForceShowNavigationBarEnabled =
-                        mForceShowNavigationBarSettingsObserver.isEnabled();
+                        mKidsModeSettingsObserver.isEnabled();
                 updateSystemBarAttributes();
             }
         });
-        mForceShowNavigationBarEnabled = mForceShowNavigationBarSettingsObserver.isEnabled();
-        mHandler.post(mForceShowNavigationBarSettingsObserver::register);
+        mForceShowNavigationBarEnabled = mKidsModeSettingsObserver.isEnabled();
+        mHandler.post(mKidsModeSettingsObserver::register);
     }
 
     /**
@@ -2861,7 +2861,7 @@
     void release() {
         mDisplayContent.mTransitionController.unregisterLegacyListener(mAppTransitionListener);
         mHandler.post(mGestureNavigationSettingsObserver::unregister);
-        mHandler.post(mForceShowNavigationBarSettingsObserver::unregister);
+        mHandler.post(mKidsModeSettingsObserver::unregister);
         mImmersiveModeConfirmation.release();
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index fc407e6..efe617d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -445,7 +445,7 @@
         }
 
         final int taskCount = visibleTasks.size();
-        for (int i = 0; i < taskCount; i++) {
+        for (int i = taskCount - 1; i >= 0; i--) {
             final Task task = visibleTasks.get(i);
             if (skipAnimation(task)) {
                 continue;
@@ -746,7 +746,7 @@
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                         "collectTaskRemoteAnimations, target: %s", target);
             }
-        }, true);
+        }, false /* traverseTopToBottom */);
     }
 
     void logRecentsAnimationStartTime(int durationMs) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6f69e03..22714c6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2007,8 +2007,7 @@
             r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
 
             final TaskFragment organizedTf = r.getOrganizedTaskFragment();
-            // TODO: Does it make sense to only count non-finishing activities?
-            final boolean singleActivity = task.getActivityCount() == 1;
+            final boolean singleActivity = task.getNonFinishingActivityCount() == 1;
             final Task rootTask;
             if (singleActivity) {
                 rootTask = task;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index bd078d8..9ea566e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1380,14 +1380,6 @@
         return getActivity(ActivityRecord::canBeTopRunning);
     }
 
-    int getActivityCount() {
-        final int[] activityCount = new int[1];
-        forAllActivities(ar -> {
-            activityCount[0]++;
-        });
-        return activityCount[0];
-    }
-
     /**
      * Return true if any activities in this task belongs to input uid.
      */
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 4e0d84c..e0346544 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1034,19 +1034,19 @@
         // If the top activity is the resumed one, nothing to do.
         if (mResumedActivity == next && next.isState(RESUMED)
                 && taskDisplayArea.allResumedActivitiesComplete()) {
+            // Ensure the visibility gets updated before execute app transition.
+            taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
+                    false /* preserveWindows */, true /* notifyClients */);
             // Make sure we have executed any pending transitions, since there
             // should be nothing left to do at this point.
             executeAppTransition(options);
-            // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible
-            // we still want to check if the visibility of other windows have changed (e.g. bringing
-            // a fullscreen window forward to cover another freeform activity.)
-            if (taskDisplayArea.inMultiWindowMode()) {
-                if (taskDisplayArea.mDisplayContent != null
-                        && taskDisplayArea.mDisplayContent.mFocusedApp != next) {
-                    taskDisplayArea.mDisplayContent.setFocusedApp(next);
-                }
-                taskDisplayArea.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */,
-                        false /* preserveWindows */, true /* notifyClients */);
+
+            // In a multi-resumed environment, like in a freeform device, the top
+            // activity can be resumed, but it might not be the focused app.
+            // Set focused app when top activity is resumed
+            if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null
+                    && taskDisplayArea.mDisplayContent.mFocusedApp != next) {
+                taskDisplayArea.mDisplayContent.setFocusedApp(next);
             }
             ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
                     + "resumed %s", next);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index c0b4f0f..ac54293 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -585,6 +585,7 @@
         DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null;
         DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null;
         DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null;
+        DeviceConfigSession<Boolean> bgPromptAbusiveAppToBgRestricted = null;
         DeviceConfigSession<Long> bgNotificationMinInterval = null;
         DeviceConfigSession<Integer> bgBatteryExemptionTypes = null;
         DeviceConfigSession<Boolean> bgCurrentDrainDecoupleThresholds = null;
@@ -642,6 +643,14 @@
                             R.bool.config_bg_prompt_fgs_with_noti_to_bg_restricted));
             bgPromptFgsWithNotiToBgRestricted.set(true);
 
+            bgPromptAbusiveAppToBgRestricted = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    ConstantsObserver.KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED,
+                    DeviceConfig::getBoolean,
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_prompt_abusive_apps_to_bg_restricted));
+            bgPromptAbusiveAppToBgRestricted.set(true);
+
             bgNotificationMinInterval = new DeviceConfigSession<>(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                     ConstantsObserver.KEY_BG_ABUSIVE_NOTIFICATION_MINIMAL_INTERVAL,
@@ -1055,6 +1064,7 @@
             closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold);
             closeIfNotNull(bgCurrentDrainBgRestrictedThreshold);
             closeIfNotNull(bgPromptFgsWithNotiToBgRestricted);
+            closeIfNotNull(bgPromptAbusiveAppToBgRestricted);
             closeIfNotNull(bgNotificationMinInterval);
             closeIfNotNull(bgBatteryExemptionTypes);
             closeIfNotNull(bgCurrentDrainDecoupleThresholds);
@@ -1613,6 +1623,7 @@
         DeviceConfigSession<String> bgPermissionsInMonitor = null;
         DeviceConfigSession<Boolean> bgCurrentDrainHighThresholdByBgLocation = null;
         DeviceConfigSession<Boolean> bgCurrentDrainDecoupleThresholds = null;
+        DeviceConfigSession<Boolean> bgPromptAbusiveAppToBgRestricted = null;
 
         mBgRestrictionController.addAppBackgroundRestrictionListener(listener);
 
@@ -1751,6 +1762,14 @@
                     AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_DECOUPLE_THRESHOLD);
             bgCurrentDrainDecoupleThresholds.set(true);
 
+            bgPromptAbusiveAppToBgRestricted = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    ConstantsObserver.KEY_BG_PROMPT_ABUSIVE_APPS_TO_BG_RESTRICTED,
+                    DeviceConfig::getBoolean,
+                    mContext.getResources().getBoolean(
+                            R.bool.config_bg_prompt_abusive_apps_to_bg_restricted));
+            bgPromptAbusiveAppToBgRestricted.set(true);
+
             mCurrentTimeMillis = 10_000L;
             doReturn(mCurrentTimeMillis - windowMs).when(stats).getStatsStartTimestamp();
             doReturn(mCurrentTimeMillis).when(stats).getStatsEndTimestamp();
@@ -2168,6 +2187,7 @@
             closeIfNotNull(bgBatteryExemptionTypes);
             closeIfNotNull(bgPermissionMonitorEnabled);
             closeIfNotNull(bgPermissionsInMonitor);
+            closeIfNotNull(bgPromptAbusiveAppToBgRestricted);
             closeIfNotNull(bgCurrentDrainHighThresholdByBgLocation);
             closeIfNotNull(bgCurrentDrainDecoupleThresholds);
         }