Merge "Make ApiLint's new_since arg the merged txt file" into sc-dev
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index fc9e5e2..f9cdbd3 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -517,6 +517,25 @@
     }
 
     /**
+     * Unregisters the IAccessibilityManagerClient from the backing service
+     * @hide
+     */
+    public boolean removeClient() {
+        synchronized (mLock) {
+            IAccessibilityManager service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
+            try {
+                return service.removeClient(mClient, mUserId);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+            }
+        }
+        return false;
+    }
+
+    /**
      * @hide
      */
     @VisibleForTesting
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index c71ea53..078ab25 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -42,6 +42,8 @@
 
     long addClient(IAccessibilityManagerClient client, int userId);
 
+    boolean removeClient(IAccessibilityManagerClient client, int userId);
+
     List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
 
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 5e2209e..721260e 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4575,7 +4575,7 @@
                 int motionPosition = findMotionRow(y);
                 if (isGlowActive()) {
                     // Pressed during edge effect, so this is considered the same as a fling catch.
-                    mTouchMode = TOUCH_MODE_FLING;
+                    touchMode = mTouchMode = TOUCH_MODE_FLING;
                 } else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
                     // User clicked on an actual view (and was not stopping a fling).
                     // Remember where the motion event started
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 115c266..212fdca 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -116,6 +116,13 @@
     }
 
     @Test
+    public void testRemoveManager() throws Exception {
+        AccessibilityManager manager = createManager(WITH_A11Y_ENABLED);
+        manager.removeClient();
+        verify(mMockService).removeClient(manager.getClient(), UserHandle.USER_CURRENT);
+    }
+
+    @Test
     public void testGetAccessibilityServiceList() throws Exception {
         // create a list of installed accessibility services the mock service returns
         List<AccessibilityServiceInfo> expectedServices = new ArrayList<>();
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
deleted file mode 100644
index 2104be4..0000000
--- a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_circle.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 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.
-  -->
-<!--
-    The transparent circle outline that encircles the bubbles when they're in the dismiss target.
--->
-<shape
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval">
-
-    <stroke
-        android:width="1dp"
-        android:color="#66FFFFFF" />
-
-    <solid android:color="#B3000000" />
-</shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml b/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
deleted file mode 100644
index ff8fede..0000000
--- a/libs/WindowManager/Shell/res/drawable/bubble_dismiss_icon.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-  ~ Copyright (C) 2020 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.
-  -->
-<!-- The 'X' bubble dismiss icon. This is just ic_close with a stroke. -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
-        android:fillColor="#FFFFFFFF"
-        android:strokeColor="#FF000000"/>
-</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml b/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
index 7809c83..f7fda36 100644
--- a/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/dismiss_circle_background.xml
@@ -20,9 +20,8 @@
     android:shape="oval">
 
     <stroke
-        android:width="1dp"
-        android:color="#AAFFFFFF" />
+        android:width="2dp"
+        android:color="@android:color/system_accent1_600" />
 
-    <solid android:color="#77000000" />
-
+    <solid android:color="@android:color/system_accent1_600" />
 </shape>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
index 6045626..62285e6 100644
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
+++ b/libs/WindowManager/Shell/res/drawable/pip_ic_close_white.xml
@@ -21,5 +21,5 @@
         android:viewportHeight="24.0">
     <path
         android:pathData="M19.000000,6.400000l-1.400000,-1.400000 -5.600000,5.600000 -5.600000,-5.600000 -1.400000,1.400000 5.600000,5.600000 -5.600000,5.600000 1.400000,1.400000 5.600000,-5.600000 5.600000,5.600000 1.400000,-1.400000 -5.600000,-5.600000z"
-        android:fillColor="#FFFFFFFF"/>
+        android:fillColor="@android:color/system_neutral1_50"/>
 </vector>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml b/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
deleted file mode 100644
index f5cd727..0000000
--- a/libs/WindowManager/Shell/res/layout/bubble_dismiss_target.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<!--
-  ~ Copyright (C) 2019 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
-  -->
-<!-- Bubble dismiss target consisting of an X icon and the text 'Dismiss'. -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="@dimen/floating_dismiss_gradient_height"
-    android:layout_gravity="bottom|center_horizontal">
-
-    <FrameLayout
-        android:id="@+id/bubble_dismiss_circle"
-        android:layout_width="@dimen/bubble_dismiss_encircle_size"
-        android:layout_height="@dimen/bubble_dismiss_encircle_size"
-        android:layout_gravity="center"
-        android:background="@drawable/bubble_dismiss_circle" />
-
-    <LinearLayout
-        android:id="@+id/bubble_dismiss_icon_container"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center"
-        android:paddingBottom="@dimen/bubble_dismiss_target_padding_y"
-        android:paddingTop="@dimen/bubble_dismiss_target_padding_y"
-        android:paddingLeft="@dimen/bubble_dismiss_target_padding_x"
-        android:paddingRight="@dimen/bubble_dismiss_target_padding_x"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:orientation="horizontal">
-
-        <ImageView
-            android:id="@+id/bubble_dismiss_close_icon"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:src="@drawable/bubble_dismiss_icon" />
-    </LinearLayout>
-</FrameLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 3caff35..dddf2c1 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -15,7 +15,8 @@
     limitations under the License.
 -->
 <resources>
-    <dimen name="dismiss_circle_size">52dp</dimen>
+    <dimen name="dismiss_circle_size">96dp</dimen>
+    <dimen name="dismiss_circle_small">60dp</dimen>
 
     <!-- The height of the gradient indicating the dismiss edge when moving a PIP. -->
     <dimen name="floating_dismiss_gradient_height">250dp</dimen>
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 7e48a7e..d821c6f 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
@@ -34,10 +34,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Outline;
-import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -192,8 +189,7 @@
     private final BubbleController mBubbleController;
     private final BubbleData mBubbleData;
 
-    private final ValueAnimator mDesaturateAndDarkenAnimator;
-    private final Paint mDesaturateAndDarkenPaint = new Paint();
+    private final ValueAnimator mDismissBubbleAnimator;
 
     private PhysicsAnimationLayout mBubbleContainer;
     private StackAnimationController mStackAnimationController;
@@ -330,8 +326,8 @@
     private boolean mIsExpansionAnimating = false;
     private boolean mIsBubbleSwitchAnimating = false;
 
-    /** The view to desaturate/darken when magneted to the dismiss target. */
-    @Nullable private View mDesaturateAndDarkenTargetView;
+    /** The view to shrink and apply alpha to when magneted to the dismiss target. */
+    @Nullable private View mViewBeingDismissed;
 
     private Rect mTempRect = new Rect();
 
@@ -415,8 +411,7 @@
                     if (mExpandedAnimationController.getDraggedOutBubble() == null) {
                         return;
                     }
-
-                    animateDesaturateAndDarken(
+                    animateDismissBubble(
                             mExpandedAnimationController.getDraggedOutBubble(), true);
                 }
 
@@ -426,8 +421,7 @@
                     if (mExpandedAnimationController.getDraggedOutBubble() == null) {
                         return;
                     }
-
-                    animateDesaturateAndDarken(
+                    animateDismissBubble(
                             mExpandedAnimationController.getDraggedOutBubble(), false);
 
                     if (wasFlungOut) {
@@ -459,14 +453,13 @@
                 @Override
                 public void onStuckToTarget(
                         @NonNull MagnetizedObject.MagneticTarget target) {
-                    animateDesaturateAndDarken(mBubbleContainer, true);
+                    animateDismissBubble(mBubbleContainer, true);
                 }
 
                 @Override
                 public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
                         float velX, float velY, boolean wasFlungOut) {
-                    animateDesaturateAndDarken(mBubbleContainer, false);
-
+                    animateDismissBubble(mBubbleContainer, false);
                     if (wasFlungOut) {
                         mStackAnimationController.flingStackThenSpringToEdge(
                                 mStackAnimationController.getStackPosition().x, velX, velY);
@@ -481,11 +474,10 @@
                     mStackAnimationController.animateStackDismissal(
                             mDismissView.getHeight() /* translationYBy */,
                             () -> {
-                                resetDesaturationAndDarken();
+                                resetDismissAnimator();
                                 dismissMagnetizedObject();
                             }
                     );
-
                     mDismissView.hide();
                 }
             };
@@ -836,17 +828,7 @@
                 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
         mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring);
 
-        mDismissView = new DismissView(context);
-        addView(mDismissView);
-
-        final ContentResolver contentResolver = getContext().getContentResolver();
-        final int dismissRadius = Settings.Secure.getInt(
-                contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
-
-        // Save the MagneticTarget instance for the newly set up view - we'll add this to the
-        // MagnetizedObjects.
-        mMagneticTarget = new MagnetizedObject.MagneticTarget(
-                mDismissView.getCircle(), dismissRadius);
+        setUpDismissView();
 
         setClipChildren(false);
         setFocusable(true);
@@ -891,6 +873,7 @@
                         mRelativeStackPositionBeforeRotation = null;
                     }
 
+                    setUpDismissView();
                     if (mIsExpanded) {
                         // Re-draw bubble row and pointer for new orientation.
                         beforeExpandedViewAnimation();
@@ -905,30 +888,23 @@
                     }
                     removeOnLayoutChangeListener(mOrientationChangedListener);
                 };
-
-        final ColorMatrix animatedMatrix = new ColorMatrix();
-        final ColorMatrix darkenMatrix = new ColorMatrix();
-
-        mDesaturateAndDarkenAnimator = ValueAnimator.ofFloat(1f, 0f);
-        mDesaturateAndDarkenAnimator.addUpdateListener(animation -> {
+        final float maxDismissSize = getResources().getDimensionPixelSize(
+                R.dimen.dismiss_circle_size);
+        final float minDismissSize = getResources().getDimensionPixelSize(
+                R.dimen.dismiss_circle_small);
+        final float sizePercent = minDismissSize / maxDismissSize;
+        mDismissBubbleAnimator = ValueAnimator.ofFloat(1f, 0f);
+        mDismissBubbleAnimator.addUpdateListener(animation -> {
             final float animatedValue = (float) animation.getAnimatedValue();
-            animatedMatrix.setSaturation(animatedValue);
-
-            final float animatedDarkenValue = (1f - animatedValue) * DARKEN_PERCENT;
-            darkenMatrix.setScale(
-                    1f - animatedDarkenValue /* red */,
-                    1f - animatedDarkenValue /* green */,
-                    1f - animatedDarkenValue /* blue */,
-                    1f /* alpha */);
-
-            // Concat the matrices so that the animatedMatrix both desaturates and darkens.
-            animatedMatrix.postConcat(darkenMatrix);
-
-            // Update the paint and apply it to the bubble container.
-            mDesaturateAndDarkenPaint.setColorFilter(new ColorMatrixColorFilter(animatedMatrix));
-
-            if (mDesaturateAndDarkenTargetView != null) {
-                mDesaturateAndDarkenTargetView.setLayerPaint(mDesaturateAndDarkenPaint);
+            if (mDismissView != null) {
+                mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f);
+                mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f);
+                final float scaleValue = Math.max(animatedValue, sizePercent);
+                mDismissView.getCircle().setScaleX(scaleValue);
+                mDismissView.getCircle().setScaleY(scaleValue);
+            }
+            if (mViewBeingDismissed != null) {
+                mViewBeingDismissed.setAlpha(Math.max(animatedValue, 0.7f));
             }
         });
 
@@ -1048,6 +1024,23 @@
         }
     };
 
+    private void setUpDismissView() {
+        if (mDismissView != null) {
+            removeView(mDismissView);
+        }
+        mDismissView = new DismissView(getContext());
+        addView(mDismissView);
+
+        final ContentResolver contentResolver = getContext().getContentResolver();
+        final int dismissRadius = Settings.Secure.getInt(
+                contentResolver, "bubble_dismiss_radius", mBubbleSize * 2 /* default */);
+
+        // Save the MagneticTarget instance for the newly set up view - we'll add this to the
+        // MagnetizedObjects.
+        mMagneticTarget = new MagnetizedObject.MagneticTarget(
+                mDismissView.getCircle(), dismissRadius);
+    }
+
     // TODO: Create ManageMenuView and move setup / animations there
     private void setUpManageMenu() {
         if (mManageMenu != null) {
@@ -1217,6 +1210,7 @@
     public void onThemeChanged() {
         setUpFlyout();
         setUpManageMenu();
+        setUpDismissView();
         updateOverflow();
         updateUserEdu();
         updateExpandedViewTheme();
@@ -1256,6 +1250,7 @@
         updateOverflow();
         setUpManageMenu();
         setUpFlyout();
+        setUpDismissView();
         mBubbleSize = mPositioner.getBubbleSize();
         for (Bubble b : mBubbleData.getBubbles()) {
             if (b.getIconView() == null) {
@@ -2264,42 +2259,46 @@
         }
     }
 
-    /** Prepares and starts the desaturate/darken animation on the bubble stack. */
-    private void animateDesaturateAndDarken(View targetView, boolean desaturateAndDarken) {
-        mDesaturateAndDarkenTargetView = targetView;
+    /** Prepares and starts the dismiss animation on the bubble stack. */
+    private void animateDismissBubble(View targetView, boolean applyAlpha) {
+        mViewBeingDismissed = targetView;
 
-        if (mDesaturateAndDarkenTargetView == null) {
+        if (mViewBeingDismissed == null) {
             return;
         }
-
-        if (desaturateAndDarken) {
-            // Use the animated paint for the bubbles.
-            mDesaturateAndDarkenTargetView.setLayerType(
-                    View.LAYER_TYPE_HARDWARE, mDesaturateAndDarkenPaint);
-            mDesaturateAndDarkenAnimator.removeAllListeners();
-            mDesaturateAndDarkenAnimator.start();
+        if (applyAlpha) {
+            mDismissBubbleAnimator.removeAllListeners();
+            mDismissBubbleAnimator.start();
         } else {
-            mDesaturateAndDarkenAnimator.removeAllListeners();
-            mDesaturateAndDarkenAnimator.addListener(new AnimatorListenerAdapter() {
+            mDismissBubbleAnimator.removeAllListeners();
+            mDismissBubbleAnimator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     super.onAnimationEnd(animation);
-                    // Stop using the animated paint.
-                    resetDesaturationAndDarken();
+                    resetDismissAnimator();
+                }
+
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    super.onAnimationCancel(animation);
+                    resetDismissAnimator();
                 }
             });
-            mDesaturateAndDarkenAnimator.reverse();
+            mDismissBubbleAnimator.reverse();
         }
     }
 
-    private void resetDesaturationAndDarken() {
+    private void resetDismissAnimator() {
+        mDismissBubbleAnimator.removeAllListeners();
+        mDismissBubbleAnimator.cancel();
 
-        mDesaturateAndDarkenAnimator.removeAllListeners();
-        mDesaturateAndDarkenAnimator.cancel();
-
-        if (mDesaturateAndDarkenTargetView != null) {
-            mDesaturateAndDarkenTargetView.setLayerType(View.LAYER_TYPE_NONE, null);
-            mDesaturateAndDarkenTargetView = null;
+        if (mViewBeingDismissed != null) {
+            mViewBeingDismissed.setAlpha(1f);
+            mViewBeingDismissed = null;
+        }
+        if (mDismissView != null) {
+            mDismissView.getCircle().setScaleX(1f);
+            mDismissView.getCircle().setScaleY(1f);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 04b5ad6..0a1cd22 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -67,8 +67,6 @@
     fun show() {
         if (isShowing) return
         isShowing = true
-        bringToFront()
-        setZ(Short.MAX_VALUE - 1f)
         setVisibility(View.VISIBLE)
         (getBackground() as TransitionDrawable).startTransition(DISMISS_SCRIM_FADE_MS)
         animator.cancel()
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 0049461..e1b198c 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
@@ -379,6 +379,10 @@
         }
     }
 
+    public SurfaceControl getSurfaceControl() {
+        return mLeash;
+    }
+
     private void setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params,
             ActivityInfo activityInfo) {
         mPipBoundsState.setBoundsStateForEntry(componentName,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
index c26b686..1da9577 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java
@@ -26,8 +26,10 @@
 import android.graphics.drawable.TransitionDrawable;
 import android.view.Gravity;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
@@ -47,7 +49,7 @@
 /**
  * Handler of all Magnetized Object related code for PiP.
  */
-public class PipDismissTargetHandler {
+public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListener {
 
     /* The multiplier to apply scale the target size by when applying the magnetic field radius */
     private static final float MAGNETIC_FIELD_RADIUS_MULTIPLIER = 1.25f;
@@ -92,6 +94,9 @@
     private int mDismissAreaHeight;
     private float mMagneticFieldRadiusPercent = 1f;
 
+    private SurfaceControl mTaskLeash;
+    private boolean mHasDismissTargetSurface;
+
     private final Context mContext;
     private final PipMotionHelper mMotionHelper;
     private final PipUiEventLogger mPipUiEventLogger;
@@ -167,6 +172,14 @@
         mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView);
     }
 
+    @Override
+    public boolean onPreDraw() {
+        mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+        mHasDismissTargetSurface = true;
+        updateDismissTargetLayer();
+        return true;
+    }
+
     /**
      * Potentially start consuming future motion events if PiP is currently near the magnetized
      * object.
@@ -207,12 +220,31 @@
                         * MAGNETIC_FIELD_RADIUS_MULTIPLIER));
     }
 
+    public void setTaskLeash(SurfaceControl taskLeash) {
+        mTaskLeash = taskLeash;
+    }
+
+    private void updateDismissTargetLayer() {
+        if (!mHasDismissTargetSurface || mTaskLeash == null) {
+            // No dismiss target surface, can just return
+            return;
+        }
+
+        // Put the dismiss target behind the task
+        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        t.setRelativeLayer(mTargetViewContainer.getViewRootImpl().getSurfaceControl(),
+                mTaskLeash, -1);
+        t.apply();
+    }
+
     /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */
     public void createOrUpdateDismissTarget() {
         if (!mTargetViewContainer.isAttachedToWindow()) {
             mMagneticTargetAnimator.cancel();
 
             mTargetViewContainer.setVisibility(View.INVISIBLE);
+            mTargetViewContainer.getViewTreeObserver().removeOnPreDrawListener(this);
+            mHasDismissTargetSurface = false;
 
             try {
                 mWindowManager.addView(mTargetViewContainer, getDismissTargetLayoutParams());
@@ -259,9 +291,9 @@
         createOrUpdateDismissTarget();
 
         if (mTargetViewContainer.getVisibility() != View.VISIBLE) {
-
             mTargetView.setTranslationY(mTargetViewContainer.getHeight());
             mTargetViewContainer.setVisibility(View.VISIBLE);
+            mTargetViewContainer.getViewTreeObserver().addOnPreDrawListener(this);
 
             // Cancel in case we were in the middle of animating it out.
             mMagneticTargetAnimator.cancel();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
index 15e7f07..604ebc0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java
@@ -40,6 +40,7 @@
 import androidx.dynamicanimation.animation.AnimationHandler;
 import androidx.dynamicanimation.animation.AnimationHandler.FrameCallbackScheduler;
 
+import com.android.wm.shell.R;
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.animation.PhysicsAnimator;
 import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -71,6 +72,8 @@
 
     /** Friction to use for PIP when it moves via physics fling animations. */
     private static final float DEFAULT_FRICTION = 1.9f;
+    /** How much of the dismiss circle size to use when scaling down PIP. **/
+    private static final float DISMISS_CIRCLE_PERCENT = 0.85f;
 
     private final Context mContext;
     private final PipTaskOrganizer mPipTaskOrganizer;
@@ -296,9 +299,17 @@
             boolean flung, Function0<Unit> after) {
         final PointF targetCenter = target.getCenterOnScreen();
 
-        final float desiredWidth = getBounds().width() / 2;
-        final float desiredHeight = getBounds().height() / 2;
+        // PIP should fit in the circle
+        final float dismissCircleSize = mContext.getResources().getDimensionPixelSize(
+                R.dimen.dismiss_circle_size);
 
+        final float width = getBounds().width();
+        final float height = getBounds().height();
+        final float ratio = width / height;
+
+        // Width should be a little smaller than the circle size.
+        final float desiredWidth = dismissCircleSize * DISMISS_CIRCLE_PERCENT;
+        final float desiredHeight = desiredWidth / ratio;
         final float destinationX = targetCenter.x - (desiredWidth / 2f);
         final float destinationY = targetCenter.y - (desiredHeight / 2f);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index f0ea465..b1086c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -75,6 +75,7 @@
     private final @NonNull PipBoundsState mPipBoundsState;
     private final PipUiEventLogger mPipUiEventLogger;
     private final PipDismissTargetHandler mPipDismissTargetHandler;
+    private final PipTaskOrganizer mPipTaskOrganizer;
     private final ShellExecutor mMainExecutor;
 
     private PipResizeGestureHandler mPipResizeGestureHandler;
@@ -173,6 +174,7 @@
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipBoundsState = pipBoundsState;
+        mPipTaskOrganizer = pipTaskOrganizer;
         mMenuController = menuController;
         mPipUiEventLogger = pipUiEventLogger;
         mFloatingContentCoordinator = floatingContentCoordinator;
@@ -799,6 +801,7 @@
             mMovementWithinDismiss = touchState.getDownTouchPosition().y
                     >= mPipBoundsState.getMovementBounds().bottom;
             mMotionHelper.setSpringingToTouch(false);
+            mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl());
 
             // If the menu is still visible then just poke the menu
             // so that it will timeout after the user stops touching it
@@ -847,6 +850,7 @@
         @Override
         public boolean onUp(PipTouchState touchState) {
             mPipDismissTargetHandler.hideDismissTargetMaybe();
+            mPipDismissTargetHandler.setTaskLeash(null);
 
             if (!touchState.isUserInteracting()) {
                 return false;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index d2947c6..bcb21d1 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -589,7 +589,7 @@
         <provider android:name=".HeapDumpProvider"
                   android:authorities="com.android.shell.heapdump"
                   android:grantUriPermissions="true"
-                  android:exported="true" />
+                  android:exported="false" />
 
         <activity
             android:name=".BugreportWarningActivity"
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 34a1452..1a00503 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -630,9 +630,9 @@
         58.0001 29.2229,56.9551 26.8945,55.195
     </string>
 
-    <!-- The radius of the enrollment progress bar, in pixels -->
+    <!-- The radius of the enrollment progress bar, in dp -->
     <integer name="config_udfpsEnrollProgressBar" translatable="false">
-        360
+        280
     </integer>
 
     <!-- package name of a built-in camera app to use to restrict implicit intent resolution
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c95101e..0b8868f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -62,10 +62,9 @@
 
     @Override
     protected void setPasswordEntryEnabled(boolean enabled) {
-        boolean wasEnabled = mPasswordEntry.isEnabled();
         mPasswordEntry.setEnabled(enabled);
         mOkButton.setEnabled(enabled);
-        if (enabled && !wasEnabled && !mPasswordEntry.hasFocus()) {
+        if (enabled && !mPasswordEntry.hasFocus()) {
             mPasswordEntry.requestFocus();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
index 52141c0..da24a8f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
@@ -31,8 +31,10 @@
     /**
      * UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
      *
-     * The call must be made from the UI thread. The callback, if provided, will also be invoked
-     * from the UI thread.
+     * This method is a no-op when some type of HBM is already enabled.
+     *
+     * This method must be called from the UI thread. The callback, if provided, will also be
+     * invoked from the UI thread.
      *
      * @param hbmType The type of HBM that should be enabled. See {@link UdfpsHbmTypes}.
      * @param surface The surface for which the HBM is requested, in case the HBM implementation
@@ -45,14 +47,14 @@
     /**
      * UdfpsView will call this to disable the HBM when the illumination is not longer needed.
      *
+     * This method is a no-op when HBM is already disabled. If HBM is enabled, this method will
+     * disable HBM for the {@code hbmType} and {@code surface} that were provided to the
+     * corresponding {@link #enableHbm(int, Surface, Runnable)}.
+     *
      * The call must be made from the UI thread. The callback, if provided, will also be invoked
      * from the UI thread.
      *
-     * @param hbmType The type of HBM that should be disabled. See {@link UdfpsHbmTypes}.
-     * @param surface The surface for which the HBM is requested, in case the HBM implementation
-     *                needs to unset special surface flags to disable the HBM. Can be null.
      * @param onHbmDisabled A runnable that will be executed once HBM is disabled.
      */
-    void disableHbm(@HbmType int hbmType, @Nullable Surface surface,
-            @Nullable Runnable onHbmDisabled);
+    void disableHbm(@Nullable Runnable onHbmDisabled);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
index 86e3ae6..6a6f57a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsSurfaceView.java
@@ -124,8 +124,9 @@
     @Override
     public void stopIllumination() {
         if (mHbmProvider != null) {
-            final Runnable onHbmDisabled = this::invalidate;
-            mHbmProvider.disableHbm(mHbmType, mHolder.getSurface(), onHbmDisabled);
+            final Runnable onHbmDisabled =
+                    (mHbmType == UdfpsHbmTypes.GLOBAL_HBM) ? this::invalidate : null;
+            mHbmProvider.disableHbm(onHbmDisabled);
         } else {
             Log.e(TAG, "stopIllumination | mHbmProvider is null");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 9dc4ac9..e11e67d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -372,6 +372,9 @@
             return;
         }
 
+        final float smallCornerRadius =
+                getResources().getDimension(R.dimen.notification_corner_radius_small)
+                /  getResources().getDimension(R.dimen.notification_corner_radius);
         final float viewEnd = viewStart + anv.getActualHeight();
         final float cornerAnimationDistance = mCornerAnimationDistance
                 * mAmbientState.getExpansionFraction();
@@ -387,7 +390,7 @@
         } else if (viewEnd < cornerAnimationTop) {
             // Fast scroll skips frames and leaves corners with unfinished rounding.
             // Reset top and bottom corners outside of animation bounds.
-            anv.setBottomRoundness(anv.isLastInSection() ? 1f : 0f,
+            anv.setBottomRoundness(anv.isLastInSection() ? 1f : smallCornerRadius,
                     false /* animate */);
         }
 
@@ -401,7 +404,7 @@
         } else if (viewStart < cornerAnimationTop) {
             // Fast scroll skips frames and leaves corners with unfinished rounding.
             // Reset top and bottom corners outside of animation bounds.
-            anv.setTopRoundness(anv.isFirstInSection() ? 1f : 0f,
+            anv.setTopRoundness(anv.isFirstInSection() ? 1f : smallCornerRadius,
                     false /* animate */);
         }
     }
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 b60ef1d..5f9c1d7 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
@@ -258,16 +258,14 @@
             }
         }
 
-        // Save (height of view before shelf, index of first view in shelf) from when shade is fully
+        // Save the index of first view in shelf from when shade is fully
         // expanded. Consider updating these states in updateContentView instead so that we don't
         // have to recalculate in every frame.
         float currentY = -scrollY;
         if (!ambientState.isOnKeyguard()) {
             currentY += mNotificationScrimPadding;
         }
-        float previousY = 0;
         state.firstViewInShelf = null;
-        state.viewHeightBeforeShelf = -1;
         for (int i = 0; i < state.visibleChildren.size(); i++) {
             final ExpandableView view = state.visibleChildren.get(i);
 
@@ -285,17 +283,8 @@
                         && !(view instanceof FooterView)
                         && state.firstViewInShelf == null) {
                     state.firstViewInShelf = view;
-                    // There might be a section gap right before the shelf.
-                    // Limit the height of the view before the shelf so that it does not include
-                    // a gap and become taller than it normally is.
-                    state.viewHeightBeforeShelf = Math.min(getMaxAllowedChildHeight(view),
-                            ambientState.getStackEndHeight()
-                            - ambientState.getShelf().getIntrinsicHeight()
-                            - mPaddingBetweenElements
-                            - previousY);
                 }
             }
-            previousY = currentY;
             currentY = currentY
                     + getMaxAllowedChildHeight(view)
                     + mPaddingBetweenElements;
@@ -454,16 +443,7 @@
             }
 
             // Clip height of view right before shelf.
-            float maxViewHeight = getMaxAllowedChildHeight(view);
-            if (ambientState.isExpansionChanging()
-                    && algorithmState.viewHeightBeforeShelf != -1) {
-                final int indexOfFirstViewInShelf = algorithmState.visibleChildren.indexOf(
-                        algorithmState.firstViewInShelf);
-                if (i == indexOfFirstViewInShelf - 1) {
-                    maxViewHeight = algorithmState.viewHeightBeforeShelf;
-                }
-            }
-            viewState.height = (int) (maxViewHeight * expansionFraction);
+            viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
         }
 
         algorithmState.mCurrentYPosition += viewState.height
@@ -737,11 +717,6 @@
         public ExpandableView firstViewInShelf;
 
         /**
-         * Height of view right before the shelf.
-         */
-        public float viewHeightBeforeShelf;
-
-        /**
          * The children from the host view which are not gone.
          */
         public final ArrayList<ExpandableView> visibleChildren = new ArrayList<>();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index f545710..3a01791 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -2203,7 +2203,7 @@
         final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
         final boolean visible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0)
                 && !mShouldUseSplitNotificationShade;
-        setQsExpansionEnabled(mAmbientState.getScrollY() == 0);
+        setQsExpansionEnabled(mAmbientState.getScrollY() == 0 && !mAmbientState.isShadeOpening());
 
         if (!mShouldUseSplitNotificationShade) {
             if (mTransitioningToFullShadeProgress > 0.0f) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9abe00f..7eecc45 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -710,6 +710,34 @@
     }
 
     @Override
+    public boolean removeClient(IAccessibilityManagerClient callback, int userId) {
+        // TODO(b/190216606): Add tracing for removeClient when implementation is the same in master
+
+        synchronized (mLock) {
+            final int resolvedUserId = mSecurityPolicy
+                    .resolveCallingUserIdEnforcingPermissionsLocked(userId);
+
+            AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
+            if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
+                boolean unregistered = mGlobalClients.unregister(callback);
+                if (DEBUG) {
+                    Slog.i(LOG_TAG,
+                            "Removed global client for pid:" + Binder.getCallingPid() + "state: "
+                                    + unregistered);
+                }
+                return unregistered;
+            } else {
+                boolean unregistered = userState.mUserClients.unregister(callback);
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Removed user client for pid:" + Binder.getCallingPid()
+                            + " and userId:" + resolvedUserId + "state: " + unregistered);
+                }
+                return unregistered;
+            }
+        }
+    }
+
+    @Override
     public void sendAccessibilityEvent(AccessibilityEvent event, int userId) {
         if (mTraceManager.isA11yTracingEnabled()) {
             mTraceManager.logTrace(LOG_TAG + ".sendAccessibilityEvent",
@@ -3271,6 +3299,14 @@
                 pw.println();
             }
             mA11yWindowManager.dump(fd, pw, args);
+            pw.println("Global client list info:{");
+            mGlobalClients.dump(pw, "    Client list ");
+            pw.println("    Registered clients:{");
+            for (int i = 0; i < mGlobalClients.getRegisteredCallbackCount(); i++) {
+                AccessibilityManagerService.Client client = (AccessibilityManagerService.Client)
+                        mGlobalClients.getRegisteredCallbackCookie(i);
+                pw.append(Arrays.toString(client.mPackageNames));
+            }
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index df349c8..0fde0de 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -51,6 +51,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -573,6 +574,15 @@
                 pw.append(componentName.toShortString());
             }
         }
+        pw.println("}");
+        pw.println("     Client list info:{");
+        mUserClients.dump(pw, "          Client list ");
+        pw.println("          Registered clients:{");
+        for (int i = 0; i < mUserClients.getRegisteredCallbackCount(); i++) {
+            AccessibilityManagerService.Client client = (AccessibilityManagerService.Client)
+                    mUserClients.getRegisteredCallbackCookie(i);
+            pw.append(Arrays.toString(client.mPackageNames));
+        }
         pw.println("}]");
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6dd4f04..afaddd9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1930,7 +1930,6 @@
                     decActiveForegroundAppLocked(smap, r);
                 }
                 r.isForeground = false;
-                resetFgsRestrictionLocked(r);
                 r.mFgsExitTime = SystemClock.uptimeMillis();
                 ServiceState stracker = r.getTracker();
                 if (stracker != null) {
@@ -1945,6 +1944,7 @@
                         FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
                         r.mFgsExitTime > r.mFgsEnterTime
                                 ? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+                resetFgsRestrictionLocked(r);
                 mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
                 if (r.app != null) {
                     mAm.updateLruProcessLocked(r.app, false, null);
@@ -4192,8 +4192,7 @@
         r.isForeground = false;
         r.foregroundId = 0;
         r.foregroundNoti = null;
-        r.mAllowWhileInUsePermissionInFgs = false;
-        r.mAllowStartForeground = REASON_DENIED;
+        resetFgsRestrictionLocked(r);
 
         // Clear start entries.
         r.clearDeliveredStartsLocked();
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 1b5483a..abb8243 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -24,6 +24,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.annotation.Nullable;
 import android.app.ApplicationExitInfo;
 import android.app.ApplicationExitInfo.Reason;
@@ -287,8 +288,8 @@
         if (!mAppExitInfoLoaded.get()) {
             return;
         }
-        mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, obtainRawRecord(app))
-                .sendToTarget();
+        mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED,
+                obtainRawRecord(app, System.currentTimeMillis())).sendToTarget();
     }
 
     void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason,
@@ -300,7 +301,7 @@
             return;
         }
 
-        ApplicationExitInfo raw = obtainRawRecord(app);
+        ApplicationExitInfo raw = obtainRawRecord(app, System.currentTimeMillis());
         raw.setReason(reason);
         raw.setSubReason(subReason);
         raw.setDescription(msg);
@@ -542,7 +543,8 @@
                         return AppExitInfoTracker.FOREACH_ACTION_NONE;
                     });
 
-                    Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+                    Collections.sort(list,
+                            (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
                     int size = list.size();
                     if (maxNum > 0) {
                         size = Math.min(size, maxNum);
@@ -976,7 +978,7 @@
     }
 
     @VisibleForTesting
-    ApplicationExitInfo obtainRawRecord(ProcessRecord app) {
+    ApplicationExitInfo obtainRawRecord(ProcessRecord app, @CurrentTimeMillisLong long timestamp) {
         ApplicationExitInfo info = mRawRecordsPool.acquire();
         if (info == null) {
             info = new ApplicationExitInfo();
@@ -998,7 +1000,7 @@
             info.setImportance(procStateToImportance(app.mState.getSetProcState()));
             info.setPss(app.mProfile.getLastPss());
             info.setRss(app.mProfile.getLastRss());
-            info.setTimestamp(System.currentTimeMillis());
+            info.setTimestamp(timestamp);
         }
 
         return info;
@@ -1298,7 +1300,7 @@
                         results.add(mInfos.valueAt(i));
                     }
                     Collections.sort(results,
-                            (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+                            (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
                 } else {
                     if (maxNum == 1) {
                         // Most of the caller might be only interested with the most recent one
@@ -1318,7 +1320,7 @@
                             list.add(mInfos.valueAt(i));
                         }
                         Collections.sort(list,
-                                (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+                                (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
                         for (int i = 0; i < maxNum; i++) {
                             results.add(list.get(i));
                         }
@@ -1412,7 +1414,7 @@
             for (int i = mInfos.size() - 1; i >= 0; i--) {
                 list.add(mInfos.valueAt(i));
             }
-            Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+            Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
             int size = list.size();
             for (int i = 0; i < size; i++) {
                 list.get(i).dump(pw, prefix + "  ", "#" + i, sdf);
@@ -1629,7 +1631,8 @@
         }
     }
 
-    private static boolean isFresh(long timestamp) {
+    @VisibleForTesting
+    boolean isFresh(long timestamp) {
         // A process could be dying but being stuck in some state, i.e.,
         // being TRACED by tombstoned, thus the zygote receives SIGCHILD
         // way after we already knew the kill (maybe because we did the kill :P),
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 9a96e53..ab1da80 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -501,37 +501,38 @@
 
             mService.grantImplicitAccess(userId, null, callingUid,
                     UserHandle.getAppId(cpi.applicationInfo.uid));
-        }
 
-        if (caller != null) {
-            // The client will be waiting, and we'll notify it when the provider is ready.
-            synchronized (cpr) {
-                if (cpr.provider == null) {
-                    if (cpr.launchingApp == null) {
-                        Slog.w(TAG, "Unable to launch app "
-                                + cpi.applicationInfo.packageName + "/"
-                                + cpi.applicationInfo.uid + " for provider "
-                                + name + ": launching app became null");
-                        EventLogTags.writeAmProviderLostProcess(
-                                UserHandle.getUserId(cpi.applicationInfo.uid),
-                                cpi.applicationInfo.packageName,
-                                cpi.applicationInfo.uid, name);
-                        return null;
-                    }
+            if (caller != null) {
+                // The client will be waiting, and we'll notify it when the provider is ready.
+                synchronized (cpr) {
+                    if (cpr.provider == null) {
+                        if (cpr.launchingApp == null) {
+                            Slog.w(TAG, "Unable to launch app "
+                                    + cpi.applicationInfo.packageName + "/"
+                                    + cpi.applicationInfo.uid + " for provider "
+                                    + name + ": launching app became null");
+                            EventLogTags.writeAmProviderLostProcess(
+                                    UserHandle.getUserId(cpi.applicationInfo.uid),
+                                    cpi.applicationInfo.packageName,
+                                    cpi.applicationInfo.uid, name);
+                            return null;
+                        }
 
-                    if (conn != null) {
-                        conn.waiting = true;
+                        if (conn != null) {
+                            conn.waiting = true;
+                        }
+                        Message msg = mService.mHandler.obtainMessage(
+                                ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG);
+                        msg.obj = cpr;
+                        mService.mHandler.sendMessageDelayed(msg,
+                                ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                     }
-                    Message msg = mService.mHandler.obtainMessage(
-                            ActivityManagerService.WAIT_FOR_CONTENT_PROVIDER_TIMEOUT_MSG);
-                    msg.obj = cpr;
-                    mService.mHandler.sendMessageDelayed(msg,
-                            ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
                 }
+                // Return a holder instance even if we are waiting for the publishing of the
+                // provider, client will check for the holder.provider to see if it needs to wait
+                // for it.
+                return cpr.newHolder(conn, false);
             }
-            // Return a holder instance even if we are waiting for the publishing of the provider,
-            // client will check for the holder.provider to see if it needs to wait for it.
-            return cpr.newHolder(conn, false);
         }
 
         // Because of the provider's external client (i.e., SHELL), we'll have to wait right here.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 00a68a0..be2a63d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2684,9 +2684,9 @@
                 } else {
                     result.addAll(approvedInfos);
 
-                    // If the other profile has an app that's of equal or higher approval, add it
+                    // If the other profile has an app that's higher approval, add it
                     if (xpDomainInfo != null
-                            && xpDomainInfo.highestApprovalLevel >= highestApproval) {
+                            && xpDomainInfo.highestApprovalLevel > highestApproval) {
                         result.add(xpDomainInfo.resolveInfo);
                     }
                 }
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index f39d618..a6c93de 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -43,8 +43,6 @@
 import com.android.internal.util.function.DecFunction;
 import com.android.internal.util.function.HeptFunction;
 import com.android.internal.util.function.HexFunction;
-import com.android.internal.util.function.NonaFunction;
-import com.android.internal.util.function.OctFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.QuintFunction;
 import com.android.internal.util.function.TriFunction;
@@ -321,6 +319,17 @@
             if (appIdTags == null) {
                 appIdTags = new ArrayMap<>();
             }
+
+            // Remove any invalid tags
+            boolean nullRemoved = packageTags.remove(null);
+            boolean nullStrRemoved = packageTags.remove("null");
+            boolean emptyRemoved = packageTags.remove("");
+            if (nullRemoved || nullStrRemoved || emptyRemoved) {
+                Log.e(LOG_TAG, "Attempted to add invalid source attribution tag, removed "
+                        + "null: " + nullRemoved + " removed \"null\": " + nullStrRemoved
+                        + " removed empty string: " + emptyRemoved);
+            }
+
             appIdTags.put(packageName, packageTags);
             datastore.put(appId, appIdTags);
         } else if (appIdTags != null) {
diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
index 42a2f81..bf54bd5 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanMatrix.java
@@ -16,9 +16,12 @@
 
 package com.android.server.utils;
 
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
 import android.annotation.Nullable;
 import android.annotation.Size;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
@@ -39,13 +42,14 @@
 public class WatchedSparseBooleanMatrix extends WatchableImpl implements Snappable {
 
     /**
-     * The matrix is implemented through four arrays.  The matrix of booleans is stored in
-     * a one-dimensional {@code mValues} array.  {@code mValues} is always of size
-     * {@code mOrder * mOrder}.  Elements of {@code mValues} are addressed with
-     * arithmetic: the offset of the element {@code {row, col}} is at
-     * {@code row * mOrder + col}.  The term "storage index" applies to {@code mValues}.
-     * A storage index designates a row (column) in the underlying storage.  This is not
-     * the same as the row seen by client code.
+     * The matrix is implemented through four arrays.  First, the matrix of booleans is
+     * stored in a two-dimensional {@code mValues} array of bit-packed booleans.
+     * {@code mValues} is always of size {@code mOrder * mOrder / 8}.  The factor of 8 is
+     * present because there are 8 bits in a byte.  Elements of {@code mValues} are
+     * addressed with arithmetic: the element {@code {row, col}} is bit {@code col % 8} in
+     * byte * {@code (row * mOrder + col) / 8}.  The term "storage index" applies to
+     * {@code mValues}.  A storage index designates a row (column) in the underlying
+     * storage.  This is not the same as the row seen by client code.
      *
      * Client code addresses the matrix through indices.  These are integers that need not
      * be contiguous.  Client indices are mapped to storage indices through two linear
@@ -61,16 +65,32 @@
      *
      * Some notes:
      * <ul>
-     * <li> The matrix never shrinks.
+     * <li> The matrix does not automatically shrink but there is a compress() method that
+     *      will recover unused space.
      * <li> Equality is a very, very expesive operation.
      * </ul>
      */
 
     /**
      * mOrder is always a multiple of this value.  A  minimal matrix therefore holds 2^12
-     * values and requires 1024 bytes.
+     * values and requires 1024 bytes.  The value is visible for testing.
      */
-    private static final int STEP = 64;
+    @VisibleForTesting(visibility = PRIVATE)
+    static final int STEP = 64;
+
+    /**
+     * There are 8 bits in a byte.  The constant is defined here only to make it easy to
+     * find in the code.
+     */
+    private static final int BYTE = 8;
+
+    /**
+     * Constants that index into the string array returned by matrixToString.  The primary
+     * consumer is test code.
+     */
+    static final int STRING_KEY_INDEX = 0;
+    static final int STRING_MAP_INDEX = 1;
+    static final int STRING_INUSE_INDEX = 2;
 
     /**
      * The order of the matrix storage, including any padding.  The matrix is always
@@ -103,7 +123,7 @@
     /**
      * The boolean array.  This array is always {@code mOrder x mOrder} in size.
      */
-    private boolean[] mValues;
+    private byte[] mValues;
 
     /**
      * A convenience function called when the elements are added to or removed from the storage.
@@ -140,7 +160,7 @@
         mInUse = new boolean[mOrder];
         mKeys = ArrayUtils.newUnpaddedIntArray(mOrder);
         mMap = ArrayUtils.newUnpaddedIntArray(mOrder);
-        mValues = new boolean[mOrder * mOrder];
+        mValues = new byte[mOrder * mOrder / 8];
         mSize = 0;
     }
 
@@ -207,7 +227,7 @@
         }
         if (r >= 0 && c >= 0) {
             setValueAt(r, c, value);
-            onChanged();
+            // setValueAt() will call onChanged().
         } else {
             throw new RuntimeException("matrix overflow");
         }
@@ -232,8 +252,12 @@
     public void removeAt(int index) {
         validateIndex(index);
         mInUse[mMap[index]] = false;
+        // Remove the specified index and ensure that unused words in mKeys and mMap are
+        // always zero, to simplify the equality function.
         System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+        mKeys[mSize - 1] = 0;
         System.arraycopy(mMap, index + 1, mMap, index, mSize - (index + 1));
+        mMap[mSize - 1] = 0;
         mSize--;
         onChanged();
     }
@@ -272,6 +296,17 @@
     }
 
     /**
+     * An internal method to fetch the boolean value given the mValues row and column
+     * indices.  These are not the indices used by the *At() methods.
+     */
+    private boolean valueAtInternal(int row, int col) {
+        int element = row * mOrder + col;
+        int offset = element / BYTE;
+        int mask = 1 << (element % BYTE);
+        return (mValues[offset] & mask) != 0;
+    }
+
+    /**
      * Given a row and column, each in the range <code>0...size()-1</code>, returns the
      * value from the <code>index</code>th key-value mapping that this WatchedSparseBooleanMatrix
      * stores.
@@ -280,8 +315,22 @@
         validateIndex(rowIndex, colIndex);
         int r = mMap[rowIndex];
         int c = mMap[colIndex];
-        int element = r * mOrder + c;
-        return mValues[element];
+        return valueAtInternal(r, c);
+    }
+
+    /**
+     * An internal method to set the boolean value given the mValues row and column
+     * indices.  These are not the indices used by the *At() methods.
+     */
+    private void setValueAtInternal(int row, int col, boolean value) {
+        int element = row * mOrder + col;
+        int offset = element / BYTE;
+        byte mask = (byte) (1 << (element % BYTE));
+        if (value) {
+            mValues[offset] |= mask;
+        } else {
+            mValues[offset] &= ~mask;
+        }
     }
 
     /**
@@ -291,8 +340,7 @@
         validateIndex(rowIndex, colIndex);
         int r = mMap[rowIndex];
         int c = mMap[colIndex];
-        int element = r * mOrder + c;
-        mValues[element] = value;
+        setValueAtInternal(r, c, value);
         onChanged();
     }
 
@@ -327,12 +375,17 @@
             mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
             mMap = GrowingArrayUtils.insert(mMap, mSize, i, newIndex);
             mSize++;
+
             // Initialize the row and column corresponding to the new index.
+            int valueRow = mOrder / BYTE;
+            int offset = newIndex / BYTE;
+            byte mask = (byte) (~(1 << (newIndex % BYTE)));
+            Arrays.fill(mValues, newIndex * valueRow, (newIndex + 1) * valueRow, (byte) 0);
             for (int n = 0; n < mSize; n++) {
-                mValues[n * mOrder + newIndex] = false;
-                mValues[newIndex * mOrder + n] = false;
+                mValues[n * valueRow + offset] &= mask;
             }
-            onChanged();
+            // Do not report onChanged() from this private method.  onChanged() is the
+            // responsibility of public methods that call this one.
         }
         return i;
     }
@@ -356,6 +409,33 @@
     }
 
     /**
+     * Expand the 2D array.  This also extends the free list.
+     */
+    private void growMatrix() {
+        resizeValues(mOrder + STEP);
+    }
+
+    /**
+     * Resize the values array to the new dimension.
+     */
+    private void resizeValues(int newOrder) {
+
+        boolean[] newInuse = Arrays.copyOf(mInUse, newOrder);
+        int minOrder = Math.min(mOrder, newOrder);
+
+        byte[] newValues = new byte[newOrder * newOrder / BYTE];
+        for (int i = 0; i < minOrder; i++) {
+            int row = mOrder * i / BYTE;
+            int newRow = newOrder * i / BYTE;
+            System.arraycopy(mValues, row, newValues, newRow, minOrder / BYTE);
+        }
+
+        mInUse = newInuse;
+        mValues = newValues;
+        mOrder = newOrder;
+    }
+
+    /**
      * Find an unused storage index, mark it in-use, and return it.
      */
     private int nextFree() {
@@ -369,27 +449,82 @@
     }
 
     /**
-     * Expand the 2D array.  This also extends the free list.
+     * Return the index of the key that uses the highest row index in use.  This returns
+     * -1 if the matrix is empty.  Note that the return is an index suitable for the *At()
+     * methods.  It is not the index in the mInUse array.
      */
-    private void growMatrix() {
-        int newOrder = mOrder + STEP;
-
-        boolean[] newInuse = Arrays.copyOf(mInUse, newOrder);
-
-        boolean[] newValues = new boolean[newOrder * newOrder];
-        for (int i = 0; i < mOrder; i++) {
-            int row = mOrder * i;
-            int newRow = newOrder * i;
-            for (int j = 0; j < mOrder; j++) {
-                int index = row + j;
-                int newIndex = newRow + j;
-                newValues[newIndex] = mValues[index];
+    private int lastInuse() {
+        for (int i = mOrder - 1; i >= 0; i--) {
+            if (mInUse[i]) {
+                for (int j = 0; j < mSize; j++) {
+                    if (mMap[j] == i) {
+                        return j;
+                    }
+                }
+                throw new IndexOutOfBoundsException();
             }
         }
+        return -1;
+    }
 
-        mInUse = newInuse;
-        mValues = newValues;
-        mOrder = newOrder;
+    /**
+     * Compress the matrix by packing keys into consecutive indices.  If the compression
+     * is sufficient, the mValues array can be shrunk.
+     */
+    private void pack() {
+        if (mSize == 0 || mSize == mOrder) {
+            return;
+        }
+        // dst and src are identify raw (row, col) in mValues.  srcIndex is the index (as
+        // in the result of keyAt()) of the key being relocated.
+        for (int dst = nextFree(); dst < mSize; dst = nextFree()) {
+            int srcIndex = lastInuse();
+            int src = mMap[srcIndex];
+            mInUse[src] = false;
+            mMap[srcIndex] = dst;
+            System.arraycopy(mValues, src * mOrder / BYTE,
+                             mValues, dst * mOrder / BYTE,
+                             mOrder / BYTE);
+            int srcOffset = (src / BYTE);
+            byte srcMask = (byte) (1 << (src % BYTE));
+            int dstOffset = (dst / BYTE);
+            byte dstMask = (byte) (1 << (dst % BYTE));
+            for (int i = 0; i < mOrder; i++) {
+                if ((mValues[srcOffset] & srcMask) == 0) {
+                    mValues[dstOffset] &= ~dstMask;
+                } else {
+                    mValues[dstOffset] |= dstMask;
+                }
+                srcOffset += mOrder / BYTE;
+                dstOffset += mOrder / BYTE;
+            }
+        }
+    }
+
+    /**
+     * Shrink the matrix, if possible.
+     */
+    public void compact() {
+        pack();
+        int unused = (mOrder - mSize) / STEP;
+        if (unused > 0) {
+            resizeValues(mOrder - (unused * STEP));
+        }
+    }
+
+    /**
+     * Return a copy of the keys that are in use by the matrix.
+     */
+    public int[] keys() {
+        return Arrays.copyOf(mKeys, mSize);
+    }
+
+    /**
+     * Return the size of the 2D matrix.  This is always greater than or equal to size().
+     * This does not reflect the sizes of the meta-information arrays (such as mKeys).
+     */
+    public int capacity() {
+        return mOrder;
     }
 
     /**
@@ -398,15 +533,12 @@
     @Override
     public int hashCode() {
         int hashCode = mSize;
+        hashCode = 31 * hashCode + Arrays.hashCode(mKeys);
+        hashCode = 31 * hashCode + Arrays.hashCode(mMap);
         for (int i = 0; i < mSize; i++) {
-            hashCode = 31 * hashCode + mKeys[i];
-            hashCode = 31 * hashCode + mMap[i];
-        }
-        for (int i = 0; i < mSize; i++) {
-            int row = mMap[i] * mOrder;
+            int row = mMap[i];
             for (int j = 0; j < mSize; j++) {
-                int element = mMap[j] + row;
-                hashCode = 31 * hashCode + (mValues[element] ? 1 : 0);
+                hashCode = 31 * hashCode + (valueAtInternal(row, mMap[j]) ? 1 : 0);
             }
         }
         return hashCode;
@@ -429,20 +561,16 @@
         if (mSize != other.mSize) {
             return false;
         }
-
-        for (int i = 0; i < mSize; i++) {
-            if (mKeys[i] != other.mKeys[i]) {
-                return false;
-            }
-            if (mMap[i] != other.mMap[i]) {
-                return false;
-            }
+        if (!Arrays.equals(mKeys, other.mKeys)) {
+            // mKeys is zero padded at the end and is sorted, so the arrays can always be
+            // directly compared.
+            return false;
         }
         for (int i = 0; i < mSize; i++) {
-            int row = mMap[i] * mOrder;
+            int row = mMap[i];
             for (int j = 0; j < mSize; j++) {
-                int element = mMap[j] + row;
-                if (mValues[element] != other.mValues[element]) {
+                int col = mMap[j];
+                if (valueAtInternal(row, col) != other.valueAtInternal(row, col)) {
                     return false;
                 }
             }
@@ -451,9 +579,12 @@
     }
 
     /**
-     * Return the matrix meta information.  This is always three strings long.
+     * Return the matrix meta information.  This is always three strings long.  The
+     * strings are indexed by the constants STRING_KEY_INDEX, STRING_MAP_INDEX, and
+     * STRING_INUSE_INDEX.
      */
-    private @Size(3) String[] matrixToStringMeta() {
+    @VisibleForTesting(visibility = PRIVATE)
+    @Size(3) String[] matrixToStringMeta() {
         String[] result = new String[3];
 
         StringBuilder k = new StringBuilder();
@@ -463,7 +594,7 @@
                 k.append(" ");
             }
         }
-        result[0] = k.substring(0);
+        result[STRING_KEY_INDEX] = k.substring(0);
 
         StringBuilder m = new StringBuilder();
         for (int i = 0; i < mSize; i++) {
@@ -472,42 +603,47 @@
                 m.append(" ");
             }
         }
-        result[1] = m.substring(0);
+        result[STRING_MAP_INDEX] = m.substring(0);
 
         StringBuilder u = new StringBuilder();
         for (int i = 0; i < mOrder; i++) {
             u.append(mInUse[i] ? "1" : "0");
         }
-        result[2] = u.substring(0);
+        result[STRING_INUSE_INDEX] = u.substring(0);
         return result;
     }
 
     /**
      * Return the matrix as an array of strings.  There is one string per row.  Each
-     * string has a '1' or a '0' in the proper column.
+     * string has a '1' or a '0' in the proper column.  This is the raw data indexed by
+     * row/column disregarding the key map.
      */
-    private String[] matrixToStringRaw() {
+    @VisibleForTesting(visibility = PRIVATE)
+    String[] matrixToStringRaw() {
         String[] result = new String[mOrder];
         for (int i = 0; i < mOrder; i++) {
-            int row = i * mOrder;
             StringBuilder line = new StringBuilder(mOrder);
             for (int j = 0; j < mOrder; j++) {
-                int element = row + j;
-                line.append(mValues[element] ? "1" : "0");
+                line.append(valueAtInternal(i, j) ? "1" : "0");
             }
             result[i] = line.substring(0);
         }
         return result;
     }
 
-    private String[] matrixToStringCooked() {
+    /**
+     * Return the matrix as an array of strings.  There is one string per row.  Each
+     * string has a '1' or a '0' in the proper column.  This is the cooked data indexed by
+     * keys, in key order.
+     */
+    @VisibleForTesting(visibility = PRIVATE)
+    String[] matrixToStringCooked() {
         String[] result = new String[mSize];
         for (int i = 0; i < mSize; i++) {
-            int row = mMap[i] * mOrder;
+            int row = mMap[i];
             StringBuilder line = new StringBuilder(mSize);
             for (int j = 0; j < mSize; j++) {
-                int element = row + mMap[j];
-                line.append(mValues[element] ? "1" : "0");
+                line.append(valueAtInternal(row, mMap[j]) ? "1" : "0");
             }
             result[i] = line.substring(0);
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index c888e54..53f1035 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -628,7 +628,7 @@
             }
 
             // scale if the crop height winds up not matching the recommended metrics
-            needScale = wpData.mHeight != cropHint.height()
+            needScale = cropHint.height() > wpData.mHeight
                     || cropHint.height() > GLHelper.getMaxTextureSize()
                     || cropHint.width() > GLHelper.getMaxTextureSize();
 
@@ -752,7 +752,7 @@
 
                         f = new FileOutputStream(wallpaper.cropFile);
                         bos = new BufferedOutputStream(f, 32*1024);
-                        finalCrop.compress(Bitmap.CompressFormat.JPEG, 100, bos);
+                        finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
                         bos.flush();  // don't rely on the implicit flush-at-close when noting success
                         success = true;
                     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index d220444..803a0c1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -36,10 +36,12 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
+import android.annotation.CurrentTimeMillisLong;
 import android.app.ApplicationExitInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -163,14 +165,15 @@
         }
     }
 
-    private void updateExitInfo(ProcessRecord app) {
-        ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app);
+    private void updateExitInfo(ProcessRecord app, @CurrentTimeMillisLong long timestamp) {
+        ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp);
         mAppExitInfoTracker.handleNoteProcessDiedLocked(raw);
         mAppExitInfoTracker.recycleRawRecord(raw);
     }
 
-    private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg) {
-        ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app);
+    private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg,
+            @CurrentTimeMillisLong long timestamp) {
+        ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp);
         raw.setReason(reason);
         raw.setSubReason(subReason);
         raw.setDescription(msg);
@@ -190,6 +193,7 @@
 
         // Test application calls System.exit()
         doNothing().when(mAppExitInfoTracker).schedulePersistProcessExitInfo(anyBoolean());
+        doReturn(true).when(mAppExitInfoTracker).isFresh(anyLong());
 
         final int app1Uid = 10123;
         final int app1Pid1 = 12345;
@@ -216,7 +220,7 @@
         final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05,
                 (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x01};
 
-        final long now1 = System.currentTimeMillis();
+        final long now1 = 1;
         ProcessRecord app = makeProcessRecord(
                 app1Pid1,                    // pid
                 app1Uid,                     // uid
@@ -240,7 +244,7 @@
         doReturn(null)
                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
                 .remove(anyInt(), anyInt());
-        updateExitInfo(app);
+        updateExitInfo(app, now1);
 
         ArrayList<ApplicationExitInfo> list = new ArrayList<ApplicationExitInfo>();
         mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
@@ -290,11 +294,11 @@
                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
                 .remove(anyInt(), anyInt());
         noteAppKill(app, ApplicationExitInfo.REASON_USER_REQUESTED,
-                ApplicationExitInfo.SUBREASON_UNKNOWN, null);
+                ApplicationExitInfo.SUBREASON_UNKNOWN, null, now1s);
 
         // Case 2: create another app1 process record with a different pid
         sleep(1);
-        final long now2 = System.currentTimeMillis();
+        final long now2 = 2;
         app = makeProcessRecord(
                 app1Pid2,               // pid
                 app1Uid,                // uid
@@ -316,16 +320,15 @@
         doReturn(new Pair<Long, Object>(now2, Integer.valueOf(makeExitStatus(exitCode))))
                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
                 .remove(anyInt(), anyInt());
-        updateExitInfo(app);
+        updateExitInfo(app, now2);
         list.clear();
 
         // Get all the records for app1Uid
         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
         assertEquals(3, list.size());
 
-        info = list.get(0);
+        info = list.get(1);
 
-        // Verify the most recent one
         verifyApplicationExitInfo(
                 info,                                 // info
                 now2,                                 // timestamp
@@ -346,7 +349,7 @@
         assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2,
                 app1Cookie2.length));
 
-        info = list.get(1);
+        info = list.get(0);
         verifyApplicationExitInfo(
                 info,                                      // info
                 now1s,                                     // timestamp
@@ -386,7 +389,7 @@
         doReturn(new Pair<Long, Object>(now3, Integer.valueOf(makeSignalStatus(sigNum))))
                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
                 .remove(anyInt(), anyInt());
-        updateExitInfo(app);
+        updateExitInfo(app, now3);
         list.clear();
         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
 
@@ -463,7 +466,7 @@
                 app2Rss1,                    // rss
                 app2ProcessName,             // processName
                 app2PackageName);            // packageName
-        updateExitInfo(app);
+        updateExitInfo(app, now4);
         list.clear();
         mAppExitInfoTracker.getExitInfo(app2PackageName, app2UidUser2, app2PidUser2, 0, list);
         assertEquals(1, list.size());
@@ -523,9 +526,9 @@
                 app3ProcessName,         // processName
                 app3PackageName);        // packageName
         noteAppKill(app, ApplicationExitInfo.REASON_CRASH_NATIVE,
-                ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description);
+                ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description, now5);
 
-        updateExitInfo(app);
+        updateExitInfo(app, now5);
         list.clear();
         mAppExitInfoTracker.getExitInfo(app3PackageName, app3UidUser2, app3PidUser2, 0, list);
         assertEquals(1, list.size());
@@ -648,11 +651,11 @@
                 app3PackageName);            // packageName
         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid);
         noteAppKill(app, ApplicationExitInfo.REASON_CRASH,
-                ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description2);
+                ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description2, now6);
 
         assertEquals(app3Uid, mAppExitInfoTracker.mIsolatedUidRecords
                 .getUidByIsolatedUid(app3IsolatedUid).longValue());
-        updateExitInfo(app);
+        updateExitInfo(app, now6);
         assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid));
 
         list.clear();
@@ -736,9 +739,9 @@
 
         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2);
         noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
-                ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description);
+                ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description, now8);
 
-        updateExitInfo(app);
+        updateExitInfo(app, now8);
         list.clear();
         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 1, list);
         assertEquals(1, list.size());
@@ -802,8 +805,8 @@
                 traceFile, traceStart, traceEnd);
 
         noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
-                ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2);
-        updateExitInfo(app);
+                ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2, now9);
+        updateExitInfo(app, now9);
         list.clear();
         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list);
         assertEquals(1, list.size());
@@ -859,7 +862,7 @@
         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
         assertEquals(3, list.size());
 
-        info = list.get(0);
+        info = list.get(1);
 
         exitCode = 6;
         verifyApplicationExitInfo(
@@ -879,7 +882,7 @@
                 IMPORTANCE_SERVICE,                   // importance
                 null);                                // description
 
-        info = list.get(1);
+        info = list.get(0);
         verifyApplicationExitInfo(
                 info,                                      // info
                 now1s,                                     // timestamp
diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
index 5db9492..f361f4a 100644
--- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
@@ -22,7 +22,6 @@
 
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -35,7 +34,6 @@
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Random;
 
 /**
@@ -869,12 +867,34 @@
             mSeed = seed;
             mRandom = new Random(mSeed);
         }
-        public int index() {
+        public int next() {
             return mRandom.nextInt(50000);
         }
         public void reset() {
             mRandom.setSeed(mSeed);
         }
+        // This is an inefficient way to know if a value appears in an array.
+        private boolean contains(int[] s, int length, int k) {
+            for (int i = 0; i < length; i++) {
+                if (s[i] == k) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        public int[] indexes(int size) {
+            reset();
+            int[] r = new int[size];
+            for (int i = 0; i < size; i++) {
+                int key = next();
+                // Ensure the list of indices are unique.
+                while (contains(r, i, key)) {
+                    key = next();
+                }
+                r[i] = key;
+            }
+            return r;
+        }
     }
 
     // Return a value based on the row and column.  The algorithm tries to avoid simple
@@ -883,28 +903,8 @@
         return (((row * 4 + col) % 3)& 1) == 1;
     }
 
-    // This is an inefficient way to know if a value appears in an array.
-    private final boolean contains(int[] s, int length, int k) {
-        for (int i = 0; i < length; i++) {
-            if (s[i] == k) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void matrixTest(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
-        indexer.reset();
-        int[] indexes = new int[size];
-        for (int i = 0; i < size; i++) {
-            int key = indexer.index();
-            // Ensure the list of indices are unique.
-            while (contains(indexes, i, key)) {
-                key = indexer.index();
-            }
-            indexes[i] = key;
-        }
-        // Set values in the matrix.
+    // Fill a matrix
+    private void fill(WatchedSparseBooleanMatrix matrix, int size, int[] indexes) {
         for (int i = 0; i < size; i++) {
             int row = indexes[i];
             for (int j = 0; j < size; j++) {
@@ -913,21 +913,39 @@
                 matrix.put(row, col, want);
             }
         }
+    }
 
-        assertEquals(matrix.size(), size);
-
-        // Read back and verify
+    // Verify the content of a matrix.  This asserts on mismatch.  Selected indices may
+    // have been deleted.
+    private void verify(WatchedSparseBooleanMatrix matrix, int[] indexes, boolean[] absent) {
         for (int i = 0; i < matrix.size(); i++) {
             int row = indexes[i];
             for (int j = 0; j < matrix.size(); j++) {
                 int col = indexes[j];
-                boolean want = cellValue(i, j);
-                boolean actual = matrix.get(row, col);
-                String msg = String.format("matrix(%d:%d, %d:%d) == %s, expected %s",
-                                           i, row, j, col, actual, want);
-                assertEquals(msg, actual, want);
+                if (absent != null && (absent[i] || absent[j])) {
+                    boolean want = false;
+                    String msg = String.format("matrix(%d:%d, %d:%d) (deleted)", i, row, j, col);
+                    assertEquals(msg, matrix.get(row, col), false);
+                    assertEquals(msg, matrix.get(row, col, false), false);
+                    assertEquals(msg, matrix.get(row, col, true), true);
+                } else {
+                    boolean want = cellValue(i, j);
+                    String msg = String.format("matrix(%d:%d, %d:%d)", i, row, j, col);
+                    assertEquals(msg, matrix.get(row, col), want);
+                    assertEquals(msg, matrix.get(row, col, false), want);
+                    assertEquals(msg, matrix.get(row, col, true), want);
+                }
             }
         }
+    }
+
+    private void matrixGrow(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
+        int[] indexes = indexer.indexes(size);
+
+        // Set values in the matrix, then read back and verify.
+        fill(matrix, size, indexes);
+        assertEquals(matrix.size(), size);
+        verify(matrix, indexes, null);
 
         // Test the keyAt/indexOfKey methods
         for (int i = 0; i < matrix.size(); i++) {
@@ -936,17 +954,101 @@
         }
     }
 
+    private void matrixDelete(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
+        int[] indexes = indexer.indexes(size);
+        fill(matrix, size, indexes);
+
+        // Delete a bunch of rows.  Verify that reading back results in false and that
+        // contains() is false.  Recreate the rows and verify that all cells (other than
+        // the one just created) are false.
+        boolean[] absent = new boolean[size];
+        for (int i = 0; i < size; i += 13) {
+            matrix.deleteKey(indexes[i]);
+            absent[i] = true;
+        }
+        verify(matrix, indexes, absent);
+    }
+
+    private void matrixShrink(WatchedSparseBooleanMatrix matrix, int size, IndexGenerator indexer) {
+        int[] indexes = indexer.indexes(size);
+        fill(matrix, size, indexes);
+
+        int initialCapacity = matrix.capacity();
+
+        // Delete every other row, remembering which rows were deleted.  The goal is to
+        // make room for compaction.
+        boolean[] absent = new boolean[size];
+        for (int i = 0; i < size; i += 2) {
+            matrix.deleteKey(indexes[i]);
+            absent[i] = true;
+        }
+
+        matrix.compact();
+        int finalCapacity = matrix.capacity();
+        assertTrue("Matrix shrink", initialCapacity > finalCapacity);
+        assertTrue("Matrix shrink", finalCapacity - matrix.size() < matrix.STEP);
+    }
+
     @Test
     public void testWatchedSparseBooleanMatrix() {
         final String name = "WatchedSparseBooleanMatrix";
 
-        // The first part of this method tests the core matrix functionality.  The second
-        // part tests the watchable behavior.  The third part tests the snappable
-        // behavior.
+        // Test the core matrix functionality.  The three tess are meant to test various
+        // combinations of auto-grow.
         IndexGenerator indexer = new IndexGenerator(3);
-        matrixTest(new WatchedSparseBooleanMatrix(), 10, indexer);
-        matrixTest(new WatchedSparseBooleanMatrix(1000), 500, indexer);
-        matrixTest(new WatchedSparseBooleanMatrix(1000), 2000, indexer);
+        matrixGrow(new WatchedSparseBooleanMatrix(), 10, indexer);
+        matrixGrow(new WatchedSparseBooleanMatrix(1000), 500, indexer);
+        matrixGrow(new WatchedSparseBooleanMatrix(1000), 2000, indexer);
+        matrixDelete(new WatchedSparseBooleanMatrix(), 500, indexer);
+        matrixShrink(new WatchedSparseBooleanMatrix(), 500, indexer);
+
+        // Test Watchable behavior.
+        WatchedSparseBooleanMatrix matrix = new WatchedSparseBooleanMatrix();
+        WatchableTester tester = new WatchableTester(matrix, name);
+        tester.verify(0, "Initial array - no registration");
+        matrix.put(INDEX_A, INDEX_A, true);
+        tester.verify(0, "Updates with no registration");
+        tester.register();
+        tester.verify(0, "Updates with no registration");
+        matrix.put(INDEX_A, INDEX_B, true);
+        tester.verify(1, "Single cell assignment");
+        matrix.put(INDEX_A, INDEX_B, true);
+        tester.verify(2, "Single cell assignment - same value");
+        matrix.put(INDEX_C, INDEX_B, true);
+        tester.verify(3, "Single cell assignment");
+        matrix.deleteKey(INDEX_B);
+        tester.verify(4, "Delete key");
+        assertEquals(matrix.get(INDEX_B, INDEX_C), false);
+        assertEquals(matrix.get(INDEX_B, INDEX_C, false), false);
+        assertEquals(matrix.get(INDEX_B, INDEX_C, true), true);
+
+        matrix.clear();
+        tester.verify(5, "Clear");
+        assertEquals(matrix.size(), 0);
+        fill(matrix, 10, indexer.indexes(10));
+        int[] keys = matrix.keys();
+        assertEquals(keys.length, matrix.size());
+        for (int i = 0; i < matrix.size(); i++) {
+            assertEquals(matrix.keyAt(i), keys[i]);
+        }
+
+        WatchedSparseBooleanMatrix a = new WatchedSparseBooleanMatrix();
+        matrixGrow(a, 10, indexer);
+        assertEquals(a.size(), 10);
+        WatchedSparseBooleanMatrix b = new WatchedSparseBooleanMatrix();
+        matrixGrow(b, 10, indexer);
+        assertEquals(b.size(), 10);
+        assertEquals(a.equals(b), true);
+        int rowIndex = b.keyAt(3);
+        int colIndex = b.keyAt(4);
+        b.put(rowIndex, colIndex, !b.get(rowIndex, colIndex));
+        assertEquals(a.equals(b), false);
+
+        // Test Snappable behavior.
+        WatchedSparseBooleanMatrix s = a.snapshot();
+        assertEquals(a.equals(s), true);
+        a.put(rowIndex, colIndex, !a.get(rowIndex, colIndex));
+        assertEquals(a.equals(s), false);
     }
 
     @Test
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 9574d27..145d7f8 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -88,10 +88,12 @@
   COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
   VERSION_MAJOR_ATTR = 0x01010577,
   PACKAGE_TYPE_ATTR = 0x01010587,
+  USES_PERMISSION_FLAGS_ATTR = 0x01010644,
 };
 
 const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
 constexpr int kCurrentDevelopmentVersion = 10000;
+constexpr int kNeverForLocation = 0x00010000;
 
 /** Retrieves the attribute of the element with the specified attribute resource id. */
 static xml::Attribute* FindAttribute(xml::Element *el, uint32_t resd_id) {
@@ -1070,6 +1072,7 @@
   std::vector<std::string> requiredNotFeatures;
   int32_t required = true;
   int32_t maxSdkVersion = -1;
+  int32_t usesPermissionFlags = 0;
 
   void Extract(xml::Element* element) override {
     name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
@@ -1086,6 +1089,8 @@
     required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
     maxSdkVersion = GetAttributeIntegerDefault(
         FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
+    usesPermissionFlags = GetAttributeIntegerDefault(
+        FindAttribute(element, USES_PERMISSION_FLAGS_ATTR), 0);
 
     if (!name.empty()) {
       CommonFeatureGroup* common = extractor()->GetCommonFeatureGroup();
@@ -1099,6 +1104,9 @@
       if (maxSdkVersion >= 0) {
         printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
       }
+      if ((usesPermissionFlags & kNeverForLocation) != 0) {
+        printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+      }
       printer->Print("\n");
       for (const std::string& requiredFeature : requiredFeatures) {
         printer->Print(StringPrintf("  required-feature='%s'\n", requiredFeature.data()));
@@ -1111,6 +1119,9 @@
         if (maxSdkVersion >= 0) {
           printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
         }
+        if ((usesPermissionFlags & kNeverForLocation) != 0) {
+          printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+        }
         printer->Print("\n");
       }
     }
@@ -1121,6 +1132,9 @@
     if (maxSdkVersion >= 0) {
       printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
     }
+    if ((usesPermissionFlags & kNeverForLocation) != 0) {
+      printer->Print(StringPrintf(" usesPermissionFlags='neverForLocation'"));
+    }
     printer->Print(StringPrintf(" reason='%s'\n", reason.data()));
   }
 };