Merge "Deferred BOOT_COMPLETED broadcast does not mean queue is not idle." into tm-dev
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/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/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/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/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/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/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/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<>();