Merge "Rename onSessionVerificationFailure to onSessionValidationFailure"
diff --git a/api/current.txt b/api/current.txt
index 09de005..b70103b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11900,6 +11900,7 @@
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index df9db27..bed7b26 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2075,7 +2075,8 @@
                 STAGED_SESSION_NO_ERROR,
                 STAGED_SESSION_VERIFICATION_FAILED,
                 STAGED_SESSION_ACTIVATION_FAILED,
-                STAGED_SESSION_UNKNOWN})
+                STAGED_SESSION_UNKNOWN,
+                STAGED_SESSION_OTHER_ERROR})
         @Retention(RetentionPolicy.SOURCE)
         public @interface StagedSessionErrorCode{}
         /**
@@ -2101,6 +2102,12 @@
          */
         public static final int STAGED_SESSION_UNKNOWN = 3;
 
+        /**
+         * Constant indicating that a known error occurred while processing this staged session, but
+         * the error could not be matched to other categories.
+         */
+        public static final int STAGED_SESSION_OTHER_ERROR = 4;
+
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         public int sessionId;
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index fbe6a50..b0d4497 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -743,6 +743,12 @@
         @UnsupportedAppUsage
         public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
 
+        /**
+         * Returns the proportion of power consumed by the System Service
+         * calls made by this UID.
+         */
+        public abstract double getProportionalSystemServiceUsage();
+
         public abstract ControllerActivityCounter getWifiControllerActivity();
         public abstract ControllerActivityCounter getBluetoothControllerActivity();
         public abstract ControllerActivityCounter getModemControllerActivity();
@@ -2882,6 +2888,17 @@
     public abstract int getDischargeAmountScreenDozeSinceCharge();
 
     /**
+     * Returns the approximate CPU time (in microseconds) spent by the system server handling
+     * incoming service calls from apps.
+     *
+     * @param cluster the index of the CPU cluster.
+     * @param step the index of the CPU speed. This is not the actual speed of the CPU.
+     * @see com.android.internal.os.PowerProfile#getNumCpuClusters()
+     * @see com.android.internal.os.PowerProfile#getNumSpeedStepsInCpuCluster(int)
+     */
+    public abstract long getSystemServiceTimeAtCpuSpeed(int cluster, int step);
+
+    /**
      * Returns the total, last, or current battery uptime in microseconds.
      *
      * @param curTime the elapsed realtime in microseconds.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 64ddb2f..3f02d70 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1814,19 +1814,13 @@
     /**
      * Called after window layout to update the bounds surface. If the surface insets have changed
      * or the surface has resized, update the bounds surface.
-     *
-     * @param shouldReparent Whether it should reparent the bounds layer to the main SurfaceControl.
      */
-    private void updateBoundsLayer(boolean shouldReparent) {
+    private void updateBoundsLayer() {
         if (mBoundsLayer != null) {
             setBoundsLayerCrop();
-            mTransaction.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(),
-                    mSurface.getNextFrameNumber());
-
-            if (shouldReparent) {
-                mTransaction.reparent(mBoundsLayer, getRenderSurfaceControl());
-            }
-            mTransaction.apply();
+            mTransaction.deferTransactionUntil(mBoundsLayer,
+                    getRenderSurfaceControl(), mSurface.getNextFrameNumber())
+                    .apply();
         }
     }
 
@@ -2905,16 +2899,7 @@
         }
 
         if (surfaceSizeChanged || surfaceReplaced || surfaceCreated || windowAttributesChanged) {
-            // If the surface has been replaced, there's a chance the bounds layer is not parented
-            // to the new layer. When updating bounds layer, also reparent to the main VRI
-            // SurfaceControl to ensure it's correctly placed in the hierarchy.
-            //
-            // This needs to be done on the client side since WMS won't reparent the children to the
-            // new surface if it thinks the app is closing. WMS gets the signal that the app is
-            // stopping, but on the client side it doesn't get stopped since it's restarted quick
-            // enough. WMS doesn't want to keep around old children since they will leak when the
-            // client creates new children.
-            updateBoundsLayer(surfaceReplaced);
+            updateBoundsLayer();
         }
 
         final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
diff --git a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
index e814ec6..eb67191 100644
--- a/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
+++ b/core/java/android/view/accessibility/IWindowMagnificationConnection.aidl
@@ -29,7 +29,7 @@
 oneway interface IWindowMagnificationConnection {
 
     /**
-     * Enables window magnification on specifed display with specified center and scale.
+     * Enables window magnification on specified display with given center and scale and animation.
      *
      * @param displayId The logical display id.
      * @param scale magnification scale.
@@ -41,7 +41,7 @@
     void enableWindowMagnification(int displayId, float scale, float centerX, float centerY);
 
     /**
-     * Sets the scale of the window magnifier on specifed display.
+     * Sets the scale of the window magnifier on specified display.
      *
      * @param displayId The logical display id.
      * @param scale magnification scale.
@@ -49,14 +49,14 @@
     void setScale(int displayId, float scale);
 
      /**
-     * Disables window magnification on specifed display.
+     * Disables window magnification on specified display with animation.
      *
      * @param displayId The logical display id.
      */
     void disableWindowMagnification(int displayId);
 
     /**
-     * Moves the window magnifier on the specifed display.
+     * Moves the window magnifier on the specified display. It has no effect while animating.
      *
      * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
      *                current screen pixels.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index c4eb396..7683067 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -385,6 +385,7 @@
     private final SuggestionHelper mSuggestionHelper = new SuggestionHelper();
 
     private boolean mFlagCursorDragFromAnywhereEnabled;
+    private float mCursorDragDirectionMinXYRatio;
     private boolean mFlagInsertionHandleGesturesEnabled;
 
     // Specifies whether the new magnifier (with fish-eye effect) is enabled.
@@ -425,6 +426,11 @@
         mFlagCursorDragFromAnywhereEnabled = AppGlobals.getIntCoreSetting(
                 WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE,
                 WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT ? 1 : 0) != 0;
+        final int cursorDragMinAngleFromVertical = AppGlobals.getIntCoreSetting(
+                WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL,
+                WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT);
+        mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(
+                cursorDragMinAngleFromVertical);
         mFlagInsertionHandleGesturesEnabled = AppGlobals.getIntCoreSetting(
                 WidgetFlags.KEY_ENABLE_INSERTION_HANDLE_GESTURES,
                 WidgetFlags.ENABLE_INSERTION_HANDLE_GESTURES_DEFAULT ? 1 : 0) != 0;
@@ -437,6 +443,8 @@
         if (TextView.DEBUG_CURSOR) {
             logCursor("Editor", "Cursor drag from anywhere is %s.",
                     mFlagCursorDragFromAnywhereEnabled ? "enabled" : "disabled");
+            logCursor("Editor", "Cursor drag min angle from vertical is %d (= %f x/y ratio)",
+                    cursorDragMinAngleFromVertical, mCursorDragDirectionMinXYRatio);
             logCursor("Editor", "Insertion handle gestures is %s.",
                     mFlagInsertionHandleGesturesEnabled ? "enabled" : "disabled");
             logCursor("Editor", "New magnifier is %s.",
@@ -463,6 +471,11 @@
     }
 
     @VisibleForTesting
+    public void setCursorDragMinAngleFromVertical(int degreesFromVertical) {
+        mCursorDragDirectionMinXYRatio = EditorTouchState.getXYRatio(degreesFromVertical);
+    }
+
+    @VisibleForTesting
     public boolean getFlagInsertionHandleGesturesEnabled() {
         return mFlagInsertionHandleGesturesEnabled;
     }
@@ -6127,10 +6140,11 @@
                     if (mIsDraggingCursor) {
                         performCursorDrag(event);
                     } else if (mFlagCursorDragFromAnywhereEnabled
-                                && mTextView.getLayout() != null
-                                && mTextView.isFocused()
-                                && mTouchState.isMovedEnoughForDrag()
-                                && !mTouchState.isDragCloseToVertical()) {
+                            && mTextView.getLayout() != null
+                            && mTextView.isFocused()
+                            && mTouchState.isMovedEnoughForDrag()
+                            && (mTouchState.getInitialDragDirectionXYRatio()
+                            > mCursorDragDirectionMinXYRatio || mTouchState.isOnHandle())) {
                         startCursorDrag(event);
                     }
                     break;
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
index 9eb63087..7514368 100644
--- a/core/java/android/widget/EditorTouchState.java
+++ b/core/java/android/widget/EditorTouchState.java
@@ -59,7 +59,7 @@
     private boolean mMultiTapInSameArea;
 
     private boolean mMovedEnoughForDrag;
-    private boolean mIsDragCloseToVertical;
+    private float mInitialDragDirectionXYRatio;
 
     public float getLastDownX() {
         return mLastDownX;
@@ -98,8 +98,23 @@
         return mMovedEnoughForDrag;
     }
 
-    public boolean isDragCloseToVertical() {
-        return mIsDragCloseToVertical && !mIsOnHandle;
+    /**
+     * When {@link #isMovedEnoughForDrag()} is {@code true}, this function returns the x/y ratio for
+     * the initial drag direction. Smaller values indicate that the direction is closer to vertical,
+     * while larger values indicate that the direction is closer to horizontal. For example:
+     * <ul>
+     *     <li>if the drag direction is exactly vertical, this returns 0
+     *     <li>if the drag direction is exactly horizontal, this returns {@link Float#MAX_VALUE}
+     *     <li>if the drag direction is 45 deg from vertical, this returns 1
+     *     <li>if the drag direction is 30 deg from vertical, this returns 0.58 (x delta is smaller
+     *     than y delta)
+     *     <li>if the drag direction is 60 deg from vertical, this returns 1.73 (x delta is bigger
+     *     than y delta)
+     * </ul>
+     * This function never returns negative values, regardless of the direction of the drag.
+     */
+    public float getInitialDragDirectionXYRatio() {
+        return mInitialDragDirectionXYRatio;
     }
 
     public void setIsOnHandle(boolean onHandle) {
@@ -155,7 +170,7 @@
             mLastDownY = event.getY();
             mLastDownMillis = event.getEventTime();
             mMovedEnoughForDrag = false;
-            mIsDragCloseToVertical = false;
+            mInitialDragDirectionXYRatio = 0.0f;
         } else if (action == MotionEvent.ACTION_UP) {
             if (TextView.DEBUG_CURSOR) {
                 logCursor("EditorTouchState", "ACTION_UP");
@@ -164,7 +179,7 @@
             mLastUpY = event.getY();
             mLastUpMillis = event.getEventTime();
             mMovedEnoughForDrag = false;
-            mIsDragCloseToVertical = false;
+            mInitialDragDirectionXYRatio = 0.0f;
         } else if (action == MotionEvent.ACTION_MOVE) {
             if (!mMovedEnoughForDrag) {
                 float deltaX = event.getX() - mLastDownX;
@@ -174,9 +189,8 @@
                 int touchSlop = config.getScaledTouchSlop();
                 mMovedEnoughForDrag = distanceSquared > touchSlop * touchSlop;
                 if (mMovedEnoughForDrag) {
-                    // If the direction of the swipe motion is within 45 degrees of vertical, it is
-                    // considered a vertical drag.
-                    mIsDragCloseToVertical = Math.abs(deltaX) <= Math.abs(deltaY);
+                    mInitialDragDirectionXYRatio = (deltaY == 0) ? Float.MAX_VALUE :
+                            Math.abs(deltaX / deltaY);
                 }
             }
         } else if (action == MotionEvent.ACTION_CANCEL) {
@@ -185,7 +199,7 @@
             mMultiTapStatus = MultiTapStatus.NONE;
             mMultiTapInSameArea = false;
             mMovedEnoughForDrag = false;
-            mIsDragCloseToVertical = false;
+            mInitialDragDirectionXYRatio = 0.0f;
         }
     }
 
@@ -201,4 +215,27 @@
         float distanceSquared = (deltaX * deltaX) + (deltaY * deltaY);
         return distanceSquared <= maxDistance * maxDistance;
     }
+
+    /**
+     * Returns the x/y ratio corresponding to the given angle relative to vertical. Smaller angle
+     * values (ie, closer to vertical) will result in a smaller x/y ratio. For example:
+     * <ul>
+     *     <li>if the angle is 45 deg, the ratio is 1
+     *     <li>if the angle is 30 deg, the ratio is 0.58 (x delta is smaller than y delta)
+     *     <li>if the angle is 60 deg, the ratio is 1.73 (x delta is bigger than y delta)
+     * </ul>
+     * If the passed-in value is <= 0, this function returns 0. If the passed-in value is >= 90,
+     * this function returns {@link Float#MAX_VALUE}.
+     *
+     * @see #getInitialDragDirectionXYRatio()
+     */
+    public static float getXYRatio(int angleFromVerticalInDegrees) {
+        if (angleFromVerticalInDegrees <= 0) {
+            return 0.0f;
+        }
+        if (angleFromVerticalInDegrees >= 90) {
+            return Float.MAX_VALUE;
+        }
+        return (float) Math.tan(Math.toRadians(angleFromVerticalInDegrees));
+    }
 }
diff --git a/core/java/android/widget/WidgetFlags.java b/core/java/android/widget/WidgetFlags.java
index 832dd51..1a49365 100644
--- a/core/java/android/widget/WidgetFlags.java
+++ b/core/java/android/widget/WidgetFlags.java
@@ -41,6 +41,28 @@
     public static final boolean ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT = true;
 
     /**
+     * Threshold for the direction of a swipe gesture in order for it to be handled as a cursor drag
+     * rather than a scroll. The direction angle of the swipe gesture must exceed this value in
+     * order to trigger cursor drag; otherwise, the swipe will be assumed to be a scroll gesture.
+     * The value units for this flag is degrees and the valid range is [0,90] inclusive. If a value
+     * < 0 is set, 0 will be used instead; if a value > 90 is set, 90 will be used instead.
+     */
+    public static final String CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL =
+            "CursorControlFeature__min_angle_from_vertical_to_start_cursor_drag";
+
+    /**
+     * The key used in app core settings for the flag
+     * {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}.
+     */
+    public static final String KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL =
+            "widget__min_angle_from_vertical_to_start_cursor_drag";
+
+    /**
+     * Default value for the flag {@link #CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL}.
+     */
+    public static final int CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT = 45;
+
+    /**
      * The flag of finger-to-cursor distance in DP for cursor dragging.
      * The value unit is DP and the range is {0..100}. If the value is out of range, the legacy
      * value, which is based on handle size, will be used.
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 3a89dcd..49ad81b 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3134,7 +3134,9 @@
         // ends up disabled. That's because at some point the old tab's vertical scrolling is
         // disabled and the new tab's is enabled. For context, see b/159997845
         setVerticalScrollEnabled(true);
-        mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
+        if (mResolverDrawerLayout != null) {
+            mResolverDrawerLayout.scrollNestedScrollableChildBackToTop();
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index ea3d2de..eb59f0f 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -123,10 +123,15 @@
     // Flag related to Privacy Indicators
 
     /**
-     * Whether the Permissions Hub is showing.
+     * Whether to show the complete ongoing app ops chip.
      */
     public static final String PROPERTY_PERMISSIONS_HUB_ENABLED = "permissions_hub_2_enabled";
 
+    /**
+     * Whether to show app ops chip for just microphone + camera.
+     */
+    public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled";
+
     // Flags related to Assistant
 
     /**
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index b3ea118..2620ba0 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -129,6 +129,7 @@
     public double videoPowerMah;
     public double wakeLockPowerMah;
     public double wifiPowerMah;
+    public double systemServiceCpuPowerMah;
 
     //                           ****************
     // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
@@ -242,6 +243,7 @@
         videoPowerMah += other.videoPowerMah;
         proportionalSmearMah += other.proportionalSmearMah;
         totalSmearedPowerMah += other.totalSmearedPowerMah;
+        systemServiceCpuPowerMah += other.systemServiceCpuPowerMah;
     }
 
     /**
@@ -253,7 +255,8 @@
     public double sumPower() {
         totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
                 sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
-                flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah;
+                flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah
+                + systemServiceCpuPowerMah;
         totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
 
         return totalPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index b131ab8..3dfa3c3 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -133,6 +133,7 @@
     private double mMaxDrainedPower;
 
     PowerCalculator mCpuPowerCalculator;
+    SystemServicePowerCalculator mSystemServicePowerCalculator;
     PowerCalculator mWakelockPowerCalculator;
     MobileRadioPowerCalculator mMobileRadioPowerCalculator;
     PowerCalculator mWifiPowerCalculator;
@@ -396,6 +397,11 @@
         }
         mCpuPowerCalculator.reset();
 
+        if (mSystemServicePowerCalculator == null) {
+            mSystemServicePowerCalculator = new SystemServicePowerCalculator(mPowerProfile, mStats);
+        }
+        mSystemServicePowerCalculator.reset();
+
         if (mMemoryPowerCalculator == null) {
             mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile);
         }
@@ -588,6 +594,8 @@
             mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
                     mStatsType);
             mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
+            mSystemServicePowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
+                    mStatsType);
 
             final double totalPower = app.sumPower();
             if (DEBUG && totalPower != 0) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 58ba16b..8498151 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -144,6 +144,7 @@
     private static final boolean DEBUG = false;
     public static final boolean DEBUG_ENERGY = false;
     private static final boolean DEBUG_ENERGY_CPU = DEBUG_ENERGY;
+    private static final boolean DEBUG_BINDER_STATS = true;
     private static final boolean DEBUG_MEMORY = false;
     private static final boolean DEBUG_HISTORY = false;
     private static final boolean USE_OLD_HISTORY = false;   // for debugging.
@@ -154,7 +155,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    static final int VERSION = 186 + (USE_OLD_HISTORY ? 1000 : 0);
+    static final int VERSION = 187 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
@@ -218,10 +219,13 @@
             new KernelCpuUidClusterTimeReader(true);
     @VisibleForTesting
     protected KernelSingleUidTimeReader mKernelSingleUidTimeReader;
+    @VisibleForTesting
+    protected SystemServerCpuThreadReader mSystemServerCpuThreadReader;
 
     private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
             = new KernelMemoryBandwidthStats();
     private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
+
     public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
         return mKernelMemoryStats;
     }
@@ -267,6 +271,7 @@
 
     /** Container for Rail Energy Data stats. */
     private final RailStats mTmpRailStats = new RailStats();
+
     /**
      * Use a queue to delay removing UIDs from {@link KernelCpuUidUserSysTimeReader},
      * {@link KernelCpuUidActiveTimeReader}, {@link KernelCpuUidClusterTimeReader},
@@ -1007,6 +1012,16 @@
 
     private long[] mCpuFreqs;
 
+    /**
+     * Times spent by the system server threads grouped by cluster and CPU speed.
+     */
+    private LongSamplingCounter[][] mSystemServerThreadCpuTimesUs;
+
+    /**
+     * Times spent by the system server threads handling incoming binder requests.
+     */
+    private LongSamplingCounter[][] mBinderThreadCpuTimesUs;
+
     @VisibleForTesting
     protected PowerProfile mPowerProfile;
 
@@ -6131,10 +6146,77 @@
      * the power consumption to the calling app.
      */
     public void noteBinderCallStats(int workSourceUid, long incrementalCallCount,
-            Collection<BinderCallsStats.CallStat> callStats) {
+            Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
         synchronized (this) {
             getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount,
                     callStats);
+            mSystemServerCpuThreadReader.setBinderThreadNativeTids(binderThreadNativeTids);
+        }
+    }
+
+    /**
+     * Estimates the proportion of system server CPU activity handling incoming binder calls
+     * that can be attributed to each app
+     */
+    @VisibleForTesting
+    public void updateSystemServiceCallStats() {
+        // Start off by computing the average duration of recorded binder calls,
+        // regardless of which binder or transaction. We will use this as a fallback
+        // for calls that were not sampled at all.
+        int totalRecordedCallCount = 0;
+        long totalRecordedCallTimeMicros = 0;
+        for (int i = 0; i < mUidStats.size(); i++) {
+            Uid uid = mUidStats.valueAt(i);
+            ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
+            for (int j = binderCallStats.size() - 1; j >= 0; j--) {
+                BinderCallStats stats = binderCallStats.valueAt(j);
+                totalRecordedCallCount += stats.recordedCallCount;
+                totalRecordedCallTimeMicros += stats.recordedCpuTimeMicros;
+            }
+        }
+
+        long totalSystemServiceTimeMicros = 0;
+
+        // For every UID, use recorded durations of sampled binder calls to estimate
+        // the total time the system server spent handling requests from this UID.
+        for (int i = 0; i < mUidStats.size(); i++) {
+            Uid uid = mUidStats.valueAt(i);
+
+            long totalTimeForUid = 0;
+            int totalCallCountForUid = 0;
+            ArraySet<BinderCallStats> binderCallStats = uid.mBinderCallStats;
+            for (int j = binderCallStats.size() - 1; j >= 0; j--) {
+                BinderCallStats stats = binderCallStats.valueAt(j);
+                totalCallCountForUid += stats.callCount;
+                if (stats.recordedCallCount > 0) {
+                    totalTimeForUid +=
+                            stats.callCount * stats.recordedCpuTimeMicros / stats.recordedCallCount;
+                } else if (totalRecordedCallCount > 0) {
+                    totalTimeForUid +=
+                            stats.callCount * totalRecordedCallTimeMicros / totalRecordedCallCount;
+                }
+            }
+
+            if (totalCallCountForUid < uid.mBinderCallCount && totalRecordedCallCount > 0) {
+                // Estimate remaining calls, which were not tracked because of binder call
+                // stats sampling
+                totalTimeForUid +=
+                        (uid.mBinderCallCount - totalCallCountForUid) * totalRecordedCallTimeMicros
+                                / totalRecordedCallCount;
+            }
+
+            uid.mSystemServiceTimeUs = totalTimeForUid;
+            totalSystemServiceTimeMicros += totalTimeForUid;
+        }
+
+        for (int i = 0; i < mUidStats.size(); i++) {
+            Uid uid = mUidStats.valueAt(i);
+            if (totalSystemServiceTimeMicros > 0) {
+                uid.mProportionalSystemServiceUsage =
+                        (double) uid.mSystemServiceTimeUs / totalSystemServiceTimeMicros;
+            } else {
+                uid.mProportionalSystemServiceUsage = 0;
+            }
         }
     }
 
@@ -6583,7 +6665,7 @@
     /**
      * Accumulates stats for a specific binder transaction.
      */
-    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    @VisibleForTesting
     protected static class BinderCallStats {
         static final Comparator<BinderCallStats> COMPARATOR =
                 Comparator.comparing(BinderCallStats::getClassName)
@@ -6822,6 +6904,16 @@
          */
         private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>();
 
+        /**
+         * Estimated total time spent by the system server handling requests from this uid.
+         */
+        private long mSystemServiceTimeUs;
+
+        /**
+         * Estimated proportion of system server binder call CPU cost for this uid.
+         */
+        private double mProportionalSystemServiceUsage;
+
         public Uid(BatteryStatsImpl bsi, int uid) {
             mBsi = bsi;
             mUid = uid;
@@ -6899,7 +6991,6 @@
             return nullIfAllZeros(mCpuClusterTimesMs, STATS_SINCE_CHARGED);
         }
 
-
         @Override
         public long[] getCpuFreqTimes(int which, int procState) {
             if (which < 0 || which >= NUM_PROCESS_STATE) {
@@ -6934,10 +7025,16 @@
             return mBinderCallCount;
         }
 
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
         public ArraySet<BinderCallStats> getBinderCallStats() {
             return mBinderCallStats;
         }
 
+        @Override
+        public  double getProportionalSystemServiceUsage() {
+            return mProportionalSystemServiceUsage;
+        }
+
         public void addIsolatedUid(int isolatedUid) {
             if (mChildUids == null) {
                 mChildUids = new IntArray();
@@ -8029,9 +8126,12 @@
             mBinderCallCount = 0;
             mBinderCallStats.clear();
 
+            mProportionalSystemServiceUsage = 0;
+
             mLastStepUserTime = mLastStepSystemTime = 0;
             mCurStepUserTime = mCurStepSystemTime = 0;
 
+
             return !active;
         }
 
@@ -8373,28 +8473,7 @@
             mUserCpuTime.writeToParcel(out);
             mSystemCpuTime.writeToParcel(out);
 
-            if (mCpuClusterSpeedTimesUs != null) {
-                out.writeInt(1);
-                out.writeInt(mCpuClusterSpeedTimesUs.length);
-                for (LongSamplingCounter[] cpuSpeeds : mCpuClusterSpeedTimesUs) {
-                    if (cpuSpeeds != null) {
-                        out.writeInt(1);
-                        out.writeInt(cpuSpeeds.length);
-                        for (LongSamplingCounter c : cpuSpeeds) {
-                            if (c != null) {
-                                out.writeInt(1);
-                                c.writeToParcel(out);
-                            } else {
-                                out.writeInt(0);
-                            }
-                        }
-                    } else {
-                        out.writeInt(0);
-                    }
-                }
-            } else {
-                out.writeInt(0);
-            }
+            mBsi.writeCpuSpeedCountersToParcel(out, mCpuClusterSpeedTimesUs);
 
             LongSamplingCounterArray.writeToParcel(out, mCpuFreqTimeMs);
             LongSamplingCounterArray.writeToParcel(out, mScreenOffCpuFreqTimeMs);
@@ -8432,6 +8511,7 @@
             } else {
                 out.writeInt(0);
             }
+            out.writeDouble(mProportionalSystemServiceUsage);
         }
 
         void readJobCompletionsFromParcelLocked(Parcel in) {
@@ -8692,36 +8772,7 @@
             mUserCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
             mSystemCpuTime = new LongSamplingCounter(mBsi.mOnBatteryTimeBase, in);
 
-            if (in.readInt() != 0) {
-                int numCpuClusters = in.readInt();
-                if (mBsi.mPowerProfile != null && mBsi.mPowerProfile.getNumCpuClusters() != numCpuClusters) {
-                    throw new ParcelFormatException("Incompatible number of cpu clusters");
-                }
-
-                mCpuClusterSpeedTimesUs = new LongSamplingCounter[numCpuClusters][];
-                for (int cluster = 0; cluster < numCpuClusters; cluster++) {
-                    if (in.readInt() != 0) {
-                        int numSpeeds = in.readInt();
-                        if (mBsi.mPowerProfile != null &&
-                                mBsi.mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
-                            throw new ParcelFormatException("Incompatible number of cpu speeds");
-                        }
-
-                        final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
-                        mCpuClusterSpeedTimesUs[cluster] = cpuSpeeds;
-                        for (int speed = 0; speed < numSpeeds; speed++) {
-                            if (in.readInt() != 0) {
-                                cpuSpeeds[speed] = new LongSamplingCounter(
-                                        mBsi.mOnBatteryTimeBase, in);
-                            }
-                        }
-                    } else {
-                        mCpuClusterSpeedTimesUs[cluster] = null;
-                    }
-                }
-            } else {
-                mCpuClusterSpeedTimesUs = null;
-            }
+            mCpuClusterSpeedTimesUs = mBsi.readCpuSpeedCountersFromParcel(in);
 
             mCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(in, mBsi.mOnBatteryTimeBase);
             mScreenOffCpuFreqTimeMs = LongSamplingCounterArray.readFromParcel(
@@ -8762,6 +8813,8 @@
             } else {
                 mWifiRadioApWakeupCount = null;
             }
+
+            mProportionalSystemServiceUsage = in.readDouble();
         }
 
         public void noteJobsDeferredLocked(int numDeferred, long sinceLast) {
@@ -9904,7 +9957,6 @@
             UserInfoProvider userInfoProvider) {
         init(clocks);
 
-
         if (systemDir == null) {
             mStatsFile = null;
             mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer);
@@ -10046,6 +10098,8 @@
             firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
         }
 
+        mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
+
         if (mEstimatedBatteryCapacity == -1) {
             // Initialize the estimated battery capacity to a known preset one.
             mEstimatedBatteryCapacity = (int) mPowerProfile.getBatteryCapacity();
@@ -10726,6 +10780,9 @@
 
         mTmpRailStats.reset();
 
+        resetIfNotNull(mSystemServerThreadCpuTimesUs, false);
+        resetIfNotNull(mBinderThreadCpuTimesUs, false);
+
         mLastHistoryStepDetails = null;
         mLastStepCpuUserTime = mLastStepCpuSystemTime = 0;
         mCurStepCpuUserTime = mCurStepCpuSystemTime = 0;
@@ -10853,7 +10910,7 @@
         return null;
     }
 
-   /**
+    /**
      * Distribute WiFi energy info and network traffic to apps.
      * @param info The energy information from the WiFi controller.
      */
@@ -11772,6 +11829,7 @@
             for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                 mKernelCpuSpeedReaders[cluster].readDelta();
             }
+            mSystemServerCpuThreadReader.readDelta();
             return;
         }
 
@@ -11791,6 +11849,87 @@
             readKernelUidCpuClusterTimesLocked(onBattery);
             mNumAllUidCpuTimeReads += 2;
         }
+
+        updateSystemServerThreadStats();
+    }
+
+    /**
+     * Estimates the proportion of the System Server CPU activity (per cluster per speed)
+     * spent on handling incoming binder calls.
+     */
+    @VisibleForTesting
+    public void updateSystemServerThreadStats() {
+        // There are some simplifying assumptions made in this algorithm
+        // 1) We assume that if a thread handles incoming binder calls, all of its activity
+        //    is spent doing that.  Most incoming calls are handled by threads allocated
+        //    by the native layer in the binder thread pool, so this assumption is reasonable.
+        // 2) We use the aggregate CPU time spent in different threads as a proxy for the CPU
+        //    cost. In reality, in multi-core CPUs, the CPU cost may not be linearly
+        //    affected by additional threads.
+
+        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+                    mSystemServerCpuThreadReader.readDelta();
+
+        int index = 0;
+        int numCpuClusters = mPowerProfile.getNumCpuClusters();
+        if (mSystemServerThreadCpuTimesUs == null) {
+            mSystemServerThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+            mBinderThreadCpuTimesUs = new LongSamplingCounter[numCpuClusters][];
+        }
+        for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+            int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+            if (mSystemServerThreadCpuTimesUs[cluster] == null) {
+                mSystemServerThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
+                mBinderThreadCpuTimesUs[cluster] = new LongSamplingCounter[numSpeeds];
+                for (int speed = 0; speed < numSpeeds; speed++) {
+                    mSystemServerThreadCpuTimesUs[cluster][speed] =
+                            new LongSamplingCounter(mOnBatteryTimeBase);
+                    mBinderThreadCpuTimesUs[cluster][speed] =
+                            new LongSamplingCounter(mOnBatteryTimeBase);
+                }
+            }
+            for (int speed = 0; speed < numSpeeds; speed++) {
+                mSystemServerThreadCpuTimesUs[cluster][speed].addCountLocked(
+                        systemServiceCpuThreadTimes.threadCpuTimesUs[index]);
+                mBinderThreadCpuTimesUs[cluster][speed].addCountLocked(
+                        systemServiceCpuThreadTimes.binderThreadCpuTimesUs[index]);
+                index++;
+            }
+        }
+        if (DEBUG_BINDER_STATS) {
+            Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
+            long binderThreadTime = 0;
+            long totalThreadTime = 0;
+            int cpuIndex = 0;
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                StringBuilder sb = new StringBuilder();
+                sb.append("cpu").append(cpuIndex).append(": [");
+                int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+                for (int speed = 0; speed < numSpeeds; speed++) {
+                    if (speed != 0) {
+                        sb.append(", ");
+                    }
+                    long totalCount = mSystemServerThreadCpuTimesUs[cluster][speed].getCountLocked(
+                            0) / 1000;
+                    long binderCount = mBinderThreadCpuTimesUs[cluster][speed].getCountLocked(0)
+                            / 1000;
+                    sb.append(String.format("%d/%d(%.1f%%)",
+                            binderCount,
+                            totalCount,
+                            totalCount != 0 ? (double) binderCount * 100 / totalCount : 0));
+
+                    totalThreadTime += totalCount;
+                    binderThreadTime += binderCount;
+                    index++;
+                }
+                cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
+                Slog.d(TAG, sb.toString());
+            }
+            Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTime);
+            Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
+                    binderThreadTime,
+                    binderThreadTime != 0 ? (double) binderThreadTime * 100 / totalThreadTime : 0));
+        }
     }
 
     /**
@@ -12998,6 +13137,75 @@
         }
     }
 
+
+    @Override
+    public long getSystemServiceTimeAtCpuSpeed(int cluster, int step) {
+        // Estimates the time spent by the system server handling incoming binder requests.
+        //
+        // The data that we can get from the kernel is this:
+        //   - CPU duration for a (thread - cluster - CPU speed) combination
+        //   - CPU duration for a (UID - cluster - CPU speed) combination
+        //
+        // The configuration we have in the Power Profile is this:
+        //   - Average CPU power for a (cluster - CPU speed) combination.
+        //
+        // The model used by BatteryStats can be illustrated with this example:
+        //
+        // - Let's say the system server has 10 threads.
+        // - These 10 threads spent 1000 ms of CPU time in aggregate
+        // - Of the 10 threads 4 were execute exclusively incoming binder calls.
+        // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
+        // - The real time spent by the system server UID doing all of this is, say, 200 ms.
+        //
+        // We will assume that power consumption is proportional to the time spent by the CPU
+        // across all threads.  This is a crude assumption, but we don't have more detailed data.
+        // Thus,
+        //   binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime
+        //
+        // In our example,
+        //   binderRealTime = 200 * 600 / 1000 = 120ms
+        //
+        // We can then multiply this estimated time by the average power to obtain an estimate
+        // of the total power consumed by incoming binder calls for the given cluster/speed
+        // combination.
+
+        if (mSystemServerThreadCpuTimesUs == null) {
+            return 0;
+        }
+
+        if (cluster < 0 || cluster >= mSystemServerThreadCpuTimesUs.length) {
+            return 0;
+        }
+
+        final LongSamplingCounter[] threadTimesForCluster = mSystemServerThreadCpuTimesUs[cluster];
+
+        if (step < 0 || step >= threadTimesForCluster.length) {
+            return 0;
+        }
+
+        Uid systemUid = mUidStats.get(Process.SYSTEM_UID);
+        if (systemUid == null) {
+            return 0;
+        }
+
+        final long uidTimeAtCpuSpeed = systemUid.getTimeAtCpuSpeed(cluster, step,
+                BatteryStats.STATS_SINCE_CHARGED);
+        if (uidTimeAtCpuSpeed == 0) {
+            return 0;
+        }
+
+        final long uidThreadTime =
+                threadTimesForCluster[step].getCountLocked(BatteryStats.STATS_SINCE_CHARGED);
+
+        if (uidThreadTime == 0) {
+            return 0;
+        }
+
+        final long binderThreadTime = mBinderThreadCpuTimesUs[cluster][step].getCountLocked(
+                BatteryStats.STATS_SINCE_CHARGED);
+        return uidTimeAtCpuSpeed * binderThreadTime / uidThreadTime;
+    }
+
     /**
      * Retrieve the statistics object for a particular uid, creating if needed.
      */
@@ -13327,45 +13535,7 @@
             pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
             pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
         }
-        pw.println("Per UID system service calls:");
-        BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver();
-        for (int i = 0; i < size; i++) {
-            int u = mUidStats.keyAt(i);
-            Uid uid = mUidStats.get(u);
-            long binderCallCount = uid.getBinderCallCount();
-            if (binderCallCount != 0) {
-                pw.print(" ");
-                pw.print(u);
-                pw.print(" system service calls: ");
-                pw.print(binderCallCount);
-                ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats();
-                if (!binderCallStats.isEmpty()) {
-                    pw.println(", including");
-                    BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()];
-                    binderCallStats.toArray(bcss);
-                    for (BinderCallStats bcs : bcss) {
-                        bcs.ensureMethodName(nameResolver);
-                    }
-                    Arrays.sort(bcss, BinderCallStats.COMPARATOR);
-                    for (BinderCallStats callStats : bcss) {
-                        pw.print("    ");
-                        pw.print(callStats.getClassName());
-                        pw.print('#');
-                        pw.print(callStats.getMethodName());
-                        pw.print(" calls: ");
-                        pw.print(callStats.callCount);
-                        if (callStats.recordedCallCount != 0) {
-                            pw.print(" time: ");
-                            pw.print(callStats.callCount * callStats.recordedCpuTimeMicros
-                                    / callStats.recordedCallCount / 1000);
-                        }
-                        pw.println();
-                    }
-                } else {
-                    pw.println();
-                }
-            }
-        }
+
         pw.println("Per UID CPU active time in ms:");
         for (int i = 0; i < size; i++) {
             int u = mUidStats.keyAt(i);
@@ -13390,6 +13560,30 @@
                 pw.print("  "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
             }
         }
+
+        updateSystemServiceCallStats();
+        if (mSystemServerThreadCpuTimesUs != null) {
+            pw.println("Per UID System server binder time in ms:");
+            for (int i = 0; i < size; i++) {
+                int u = mUidStats.keyAt(i);
+                Uid uid = mUidStats.get(u);
+                double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
+
+                long time = 0;
+                for (int cluster = 0; cluster < mSystemServerThreadCpuTimesUs.length; cluster++) {
+                    int numSpeeds = mSystemServerThreadCpuTimesUs[cluster].length;
+                    for (int speed = 0; speed < numSpeeds; speed++) {
+                        time += getSystemServiceTimeAtCpuSpeed(cluster, speed)
+                                * proportionalSystemServiceUsage;
+                    }
+                }
+
+                pw.print("  ");
+                pw.print(u);
+                pw.print(": ");
+                pw.println(time);
+            }
+        }
     }
 
     final ReentrantLock mWriteLock = new ReentrantLock();
@@ -14908,6 +15102,9 @@
             u.readFromParcelLocked(mOnBatteryTimeBase, mOnBatteryScreenOffTimeBase, in);
             mUidStats.append(uid, u);
         }
+
+        mSystemServerThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
+        mBinderThreadCpuTimesUs = readCpuSpeedCountersFromParcel(in);
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -14923,6 +15120,8 @@
         // Need to update with current kernel wake lock counts.
         pullPendingStateUpdatesLocked();
 
+        updateSystemServiceCallStats();
+
         // Pull the clock time.  This may update the time and make a new history entry
         // if we had originally pulled a time before the RTC was set.
         getStartClockTime();
@@ -15105,6 +15304,73 @@
         } else {
             out.writeInt(0);
         }
+        writeCpuSpeedCountersToParcel(out, mSystemServerThreadCpuTimesUs);
+        writeCpuSpeedCountersToParcel(out, mBinderThreadCpuTimesUs);
+    }
+
+    private void writeCpuSpeedCountersToParcel(Parcel out, LongSamplingCounter[][] counters) {
+        if (counters == null) {
+            out.writeInt(0);
+            return;
+        }
+
+        out.writeInt(1);
+        out.writeInt(counters.length);
+        for (int i = 0; i < counters.length; i++) {
+            LongSamplingCounter[] counterArray = counters[i];
+            if (counterArray == null) {
+                out.writeInt(0);
+                continue;
+            }
+
+            out.writeInt(1);
+            out.writeInt(counterArray.length);
+            for (int j = 0; j < counterArray.length; j++) {
+                LongSamplingCounter c = counterArray[j];
+                if (c != null) {
+                    out.writeInt(1);
+                    c.writeToParcel(out);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+        }
+    }
+
+    private LongSamplingCounter[][] readCpuSpeedCountersFromParcel(Parcel in) {
+        LongSamplingCounter[][] counters;
+        if (in.readInt() != 0) {
+            int numCpuClusters = in.readInt();
+            if (mPowerProfile != null
+                    && mPowerProfile.getNumCpuClusters() != numCpuClusters) {
+                throw new ParcelFormatException("Incompatible number of cpu clusters");
+            }
+
+            counters = new LongSamplingCounter[numCpuClusters][];
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                if (in.readInt() != 0) {
+                    int numSpeeds = in.readInt();
+                    if (mPowerProfile != null
+                            && mPowerProfile.getNumSpeedStepsInCpuCluster(cluster) != numSpeeds) {
+                        throw new ParcelFormatException("Incompatible number of cpu speeds");
+                    }
+
+                    final LongSamplingCounter[] cpuSpeeds = new LongSamplingCounter[numSpeeds];
+                    counters[cluster] = cpuSpeeds;
+                    for (int speed = 0; speed < numSpeeds; speed++) {
+                        if (in.readInt() != 0) {
+                            cpuSpeeds[speed] = new LongSamplingCounter(mOnBatteryTimeBase, in);
+                        }
+                    }
+                } else {
+                    counters[cluster] = null;
+                }
+            }
+        } else {
+            counters = null;
+        }
+
+        return counters;
     }
 
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index e09ef49..201626a 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -115,7 +115,8 @@
                     if (uidEntry != null) {
                         ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats;
                         mCallStatsObserver.noteCallStats(uidEntry.workSourceUid,
-                                uidEntry.incrementalCallCount, callStats.values());
+                                uidEntry.incrementalCallCount, callStats.values(),
+                                mNativeTids.toArray());
                         uidEntry.incrementalCallCount = 0;
                         for (int j = callStats.size() - 1; j >= 0; j--) {
                             callStats.valueAt(j).incrementalCallCount = 0;
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index feb5aab..f14d5f2 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -142,7 +142,8 @@
          * Notes incoming binder call stats associated with this work source UID.
          */
         void noteCallStats(int workSourceUid, long incrementalCallCount,
-                Collection<BinderCallsStats.CallStat> callStats);
+                Collection<BinderCallsStats.CallStat> callStats,
+                int[] binderThreadNativeTids);
     }
 
     /**
diff --git a/core/java/com/android/internal/os/KernelCpuThreadReader.java b/core/java/com/android/internal/os/KernelCpuThreadReader.java
index 3407670..2ba372a 100644
--- a/core/java/com/android/internal/os/KernelCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelCpuThreadReader.java
@@ -225,19 +225,22 @@
 
     /** Set the number of frequency buckets to use */
     void setNumBuckets(int numBuckets) {
-        if (numBuckets < 1) {
-            Slog.w(TAG, "Number of buckets must be at least 1, but was " + numBuckets);
-            return;
-        }
         // If `numBuckets` hasn't changed since the last set, do nothing
         if (mFrequenciesKhz != null && mFrequenciesKhz.length == numBuckets) {
             return;
         }
-        mFrequencyBucketCreator =
-                new FrequencyBucketCreator(mProcTimeInStateReader.getFrequenciesKhz(), numBuckets);
-        mFrequenciesKhz =
-                mFrequencyBucketCreator.bucketFrequencies(
-                        mProcTimeInStateReader.getFrequenciesKhz());
+
+        final long[] frequenciesKhz = mProcTimeInStateReader.getFrequenciesKhz();
+        if (numBuckets != 0) {
+            mFrequencyBucketCreator = new FrequencyBucketCreator(frequenciesKhz, numBuckets);
+            mFrequenciesKhz = mFrequencyBucketCreator.bucketFrequencies(frequenciesKhz);
+        } else {
+            mFrequencyBucketCreator = null;
+            mFrequenciesKhz = new int[frequenciesKhz.length];
+            for (int i = 0; i < frequenciesKhz.length; i++) {
+                mFrequenciesKhz[i] = (int) frequenciesKhz[i];
+            }
+        }
     }
 
     /** Set the UID predicate for {@link #getProcessCpuUsage} */
@@ -320,8 +323,15 @@
         if (cpuUsagesLong == null) {
             return null;
         }
-        int[] cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
-
+        final int[] cpuUsages;
+        if (mFrequencyBucketCreator != null) {
+            cpuUsages = mFrequencyBucketCreator.bucketValues(cpuUsagesLong);
+        } else {
+            cpuUsages = new int[cpuUsagesLong.length];
+            for (int i = 0; i < cpuUsagesLong.length; i++) {
+                cpuUsages[i] = (int) cpuUsagesLong[i];
+            }
+        }
         return new ThreadCpuUsage(threadId, threadName, cpuUsages);
     }
 
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
new file mode 100644
index 0000000..1cdd42c
--- /dev/null
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.os;
+
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
+ * by various threads of the System Server.
+ */
+public class SystemServerCpuThreadReader {
+    private KernelCpuThreadReader mKernelCpuThreadReader;
+    private int[] mBinderThreadNativeTids;
+
+    private int[] mThreadCpuTimesUs;
+    private int[] mBinderThreadCpuTimesUs;
+    private long[] mLastThreadCpuTimesUs;
+    private long[] mLastBinderThreadCpuTimesUs;
+
+    /**
+     * Times (in microseconds) spent by the system server UID.
+     */
+    public static class SystemServiceCpuThreadTimes {
+        // All threads
+        public long[] threadCpuTimesUs;
+        // Just the threads handling incoming binder calls
+        public long[] binderThreadCpuTimesUs;
+    }
+
+    private SystemServiceCpuThreadTimes mDeltaCpuThreadTimes = new SystemServiceCpuThreadTimes();
+
+    /**
+     * Creates a configured instance of SystemServerCpuThreadReader.
+     */
+    public static SystemServerCpuThreadReader create() {
+        return new SystemServerCpuThreadReader(
+                KernelCpuThreadReader.create(0, uid -> uid == Process.myUid()));
+    }
+
+    @VisibleForTesting
+    public SystemServerCpuThreadReader(Path procPath, int systemServerUid) throws IOException {
+        this(new KernelCpuThreadReader(0, uid -> uid == systemServerUid, null, null,
+                new KernelCpuThreadReader.Injector() {
+                    @Override
+                    public int getUidForPid(int pid) {
+                        return systemServerUid;
+                    }
+                }));
+    }
+
+    @VisibleForTesting
+    public SystemServerCpuThreadReader(KernelCpuThreadReader kernelCpuThreadReader) {
+        mKernelCpuThreadReader = kernelCpuThreadReader;
+    }
+
+    public void setBinderThreadNativeTids(int[] nativeTids) {
+        mBinderThreadNativeTids = nativeTids;
+    }
+
+    /**
+     * Returns delta of CPU times, per thread, since the previous call to this method.
+     */
+    public SystemServiceCpuThreadTimes readDelta() {
+        if (mBinderThreadCpuTimesUs == null) {
+            int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequenciesKhz().length;
+            mThreadCpuTimesUs = new int[numCpuFrequencies];
+            mBinderThreadCpuTimesUs = new int[numCpuFrequencies];
+
+            mLastThreadCpuTimesUs = new long[numCpuFrequencies];
+            mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
+
+            mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
+            mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
+        }
+
+        Arrays.fill(mThreadCpuTimesUs, 0);
+        Arrays.fill(mBinderThreadCpuTimesUs, 0);
+
+        ArrayList<KernelCpuThreadReader.ProcessCpuUsage> processCpuUsage =
+                mKernelCpuThreadReader.getProcessCpuUsage();
+        int processCpuUsageSize = processCpuUsage.size();
+        for (int i = 0; i < processCpuUsageSize; i++) {
+            KernelCpuThreadReader.ProcessCpuUsage pcu = processCpuUsage.get(i);
+            ArrayList<KernelCpuThreadReader.ThreadCpuUsage> threadCpuUsages = pcu.threadCpuUsages;
+            if (threadCpuUsages != null) {
+                int threadCpuUsagesSize = threadCpuUsages.size();
+                for (int j = 0; j < threadCpuUsagesSize; j++) {
+                    KernelCpuThreadReader.ThreadCpuUsage tcu = threadCpuUsages.get(j);
+                    boolean isBinderThread = isBinderThread(tcu.threadId);
+
+                    final int len = Math.min(tcu.usageTimesMillis.length, mThreadCpuTimesUs.length);
+                    for (int k = 0; k < len; k++) {
+                        int usageTimeUs = tcu.usageTimesMillis[k] * 1000;
+                        mThreadCpuTimesUs[k] += usageTimeUs;
+                        if (isBinderThread) {
+                            mBinderThreadCpuTimesUs[k] += usageTimeUs;
+                        }
+                    }
+                }
+            }
+        }
+
+        for (int i = 0; i < mThreadCpuTimesUs.length; i++) {
+            if (mThreadCpuTimesUs[i] < mLastThreadCpuTimesUs[i]) {
+                mDeltaCpuThreadTimes.threadCpuTimesUs[i] = mThreadCpuTimesUs[i];
+                mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+            } else {
+                mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
+                        mThreadCpuTimesUs[i] - mLastThreadCpuTimesUs[i];
+                mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
+                        mBinderThreadCpuTimesUs[i] - mLastBinderThreadCpuTimesUs[i];
+            }
+            mLastThreadCpuTimesUs[i] = mThreadCpuTimesUs[i];
+            mLastBinderThreadCpuTimesUs[i] = mBinderThreadCpuTimesUs[i];
+        }
+
+        return mDeltaCpuThreadTimes;
+    }
+
+    private boolean isBinderThread(int threadId) {
+        if (mBinderThreadNativeTids != null) {
+            for (int i = 0; i < mBinderThreadNativeTids.length; i++) {
+                if (threadId == mBinderThreadNativeTids[i]) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
new file mode 100644
index 0000000..481b901
--- /dev/null
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Estimates the amount of power consumed by the System Server handling requests from
+ * a given app.
+ */
+public class SystemServicePowerCalculator extends PowerCalculator {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "SystemServicePowerCalc";
+
+    private static final long MICROSEC_IN_HR = (long) 60 * 60 * 1000 * 1000;
+
+    private final PowerProfile mPowerProfile;
+    private final BatteryStats mBatteryStats;
+    // Tracks system server CPU [cluster][speed] power in milliAmp-microseconds
+    private double[][] mSystemServicePowerMaUs;
+
+    public SystemServicePowerCalculator(PowerProfile powerProfile, BatteryStats batteryStats) {
+        mPowerProfile = powerProfile;
+        mBatteryStats = batteryStats;
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+            long rawUptimeUs, int statsType) {
+        final double proportionalUsage = u.getProportionalSystemServiceUsage();
+        if (proportionalUsage > 0) {
+            if (mSystemServicePowerMaUs == null) {
+                updateSystemServicePower();
+            }
+
+            double cpuPowerMaUs = 0;
+            int numCpuClusters = mPowerProfile.getNumCpuClusters();
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+                for (int speed = 0; speed < numSpeeds; speed++) {
+                    cpuPowerMaUs += mSystemServicePowerMaUs[cluster][speed] * proportionalUsage;
+                }
+            }
+
+            app.systemServiceCpuPowerMah = cpuPowerMaUs / MICROSEC_IN_HR;
+        }
+    }
+
+    private void updateSystemServicePower() {
+        final int numCpuClusters = mPowerProfile.getNumCpuClusters();
+        mSystemServicePowerMaUs = new double[numCpuClusters][];
+        for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+            final int numSpeeds = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+            mSystemServicePowerMaUs[cluster] = new double[numSpeeds];
+            for (int speed = 0; speed < numSpeeds; speed++) {
+                mSystemServicePowerMaUs[cluster][speed] =
+                        mBatteryStats.getSystemServiceTimeAtCpuSpeed(cluster, speed)
+                                * mPowerProfile.getAveragePowerForCpuCore(cluster, speed);
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "System service power per CPU cluster and frequency");
+            for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+                Log.d(TAG, "Cluster[" + cluster  + "]: "
+                        + Arrays.toString(mSystemServicePowerMaUs[cluster]));
+            }
+        }
+    }
+
+    @Override
+    public void reset() {
+        mSystemServicePowerMaUs = null;
+    }
+}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
index df2946c..c37a34a 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorDragTest.java
@@ -201,6 +201,38 @@
     }
 
     @Test
+    public void testCursorDrag_diagonal_thresholdConfig() throws Throwable {
+        TextView tv = mActivity.findViewById(R.id.textview);
+        Editor editor = tv.getEditorForTesting();
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 1; i <= 9; i++) {
+            sb.append("here is some text").append(i).append("\n");
+        }
+        sb.append(Strings.repeat("abcdefghij\n", 400)).append("Last");
+        String text = sb.toString();
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        int index = text.indexOf("text9");
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(index));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+
+        // Configure the drag direction threshold to require the drag to be exactly horizontal. With
+        // this set, a swipe that is slightly off horizontal should not trigger cursor drag.
+        editor.setCursorDragMinAngleFromVertical(90);
+        int startIdx = text.indexOf("5");
+        int endIdx = text.indexOf("here is some text3");
+        onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(index));
+
+        // Configure the drag direction threshold to require the drag to be 45 degrees or more from
+        // vertical. With this set, the same swipe gesture as above should now trigger cursor drag.
+        editor.setCursorDragMinAngleFromVertical(45);
+        onView(withId(R.id.textview)).perform(dragOnText(startIdx, endIdx));
+        onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(endIdx));
+    }
+
+    @Test
     public void testCursorDrag_vertical_whenTextViewContentsFitOnScreen() throws Throwable {
         String text = "012345_aaa\n"
                 + "0123456789\n"
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
index 35fd4bd..94f43de 100644
--- a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -165,7 +165,7 @@
         long event2Time = 1001;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 200f, 31f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, 180f);
 
         // Simulate an ACTION_UP event with a delay that's longer than the double-tap timeout.
         long event3Time = 5000;
@@ -280,7 +280,7 @@
         long event3Time = 1002;
         MotionEvent event3 = moveEvent(event3Time, event3Time, newX, newY);
         mTouchState.update(event3, mConfig);
-        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1003;
@@ -301,15 +301,15 @@
         long event2Time = 1002;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 174f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f);
 
         // Simulate another ACTION_MOVE event that is horizontal from the original down event.
-        // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
-        // initial direction of movement.
+        // The drag direction ratio should NOT change since it should only reflect the initial
+        // direction of movement.
         long event3Time = 1003;
         MotionEvent event3 = moveEvent(event1Time, event3Time, 200f, 0f);
         mTouchState.update(event3, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, true);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 174f);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1004;
@@ -330,15 +330,15 @@
         long event2Time = 1002;
         MotionEvent event2 = moveEvent(event1Time, event2Time, 100f, 90f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f);
 
         // Simulate another ACTION_MOVE event that is vertical from the original down event.
-        // The value of `isDragCloseToVertical` should NOT change since it should only reflect the
-        // initial direction of movement.
+        // The drag direction ratio should NOT change since it should only reflect the initial
+        // direction of movement.
         long event3Time = 1003;
         MotionEvent event3 = moveEvent(event1Time, event3Time, 0f, 200f);
         mTouchState.update(event3, mConfig);
-        assertDrag(mTouchState, 0f, 0f, 0, 0, false);
+        assertDrag(mTouchState, 0f, 0f, 0, 0, 100f / 90f);
 
         // Simulate an ACTION_UP event.
         long event4Time = 1004;
@@ -374,7 +374,7 @@
         long event2Time = 1002;
         MotionEvent event2 = moveEvent(event2Time, event2Time, 200f, 30f);
         mTouchState.update(event2, mConfig);
-        assertDrag(mTouchState, 20f, 30f, 0, 0, false);
+        assertDrag(mTouchState, 20f, 30f, 0, 0, Float.MAX_VALUE);
 
         // Simulate an ACTION_CANCEL event.
         long event3Time = 1003;
@@ -411,6 +411,84 @@
         assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
     }
 
+    @Test
+    public void testGetXYRatio() throws Exception {
+        doTestGetXYRatio(-1, 0.0f);
+        doTestGetXYRatio(0, 0.0f);
+        doTestGetXYRatio(30, 0.58f);
+        doTestGetXYRatio(45, 1.0f);
+        doTestGetXYRatio(60, 1.73f);
+        doTestGetXYRatio(90, Float.MAX_VALUE);
+        doTestGetXYRatio(91, Float.MAX_VALUE);
+    }
+
+    private void doTestGetXYRatio(int angleFromVerticalInDegrees, float expectedXYRatioRounded) {
+        float result = EditorTouchState.getXYRatio(angleFromVerticalInDegrees);
+        String msg = String.format(
+                "%d deg should give an x/y ratio of %f; actual unrounded result is %f",
+                angleFromVerticalInDegrees, expectedXYRatioRounded, result);
+        float roundedResult = (result == 0.0f || result == Float.MAX_VALUE) ? result :
+                Math.round(result * 100) / 100f;
+        assertThat(msg, roundedResult, is(expectedXYRatioRounded));
+    }
+
+    @Test
+    public void testUpdate_dragDirection() throws Exception {
+        // Simulate moving straight up.
+        doTestDragDirection(100f, 100f, 100f, 50f, 0f);
+
+        // Simulate moving straight down.
+        doTestDragDirection(100f, 100f, 100f, 150f, 0f);
+
+        // Simulate moving straight left.
+        doTestDragDirection(100f, 100f, 50f, 100f, Float.MAX_VALUE);
+
+        // Simulate moving straight right.
+        doTestDragDirection(100f, 100f, 150f, 100f, Float.MAX_VALUE);
+
+        // Simulate moving up and right, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 110f, 50f, 10f / 50f);
+
+        // Simulate moving up and right, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 150f, 90f, 50f / 10f);
+
+        // Simulate moving down and right, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 110f, 150f, 10f / 50f);
+
+        // Simulate moving down and right, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 150f, 110f, 50f / 10f);
+
+        // Simulate moving down and left, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 90f, 150f, 10f / 50f);
+
+        // Simulate moving down and left, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 50f, 110f, 50f / 10f);
+
+        // Simulate moving up and left, < 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 90f, 50f, 10f / 50f);
+
+        // Simulate moving up and left, > 45 deg from vertical.
+        doTestDragDirection(100f, 100f, 50f, 90f, 50f / 10f);
+    }
+
+    private void doTestDragDirection(float downX, float downY, float moveX, float moveY,
+            float expectedInitialDragDirectionXYRatio) {
+        EditorTouchState touchState = new EditorTouchState();
+
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1001;
+        MotionEvent event1 = downEvent(event1Time, event1Time, downX, downY);
+        touchState.update(event1, mConfig);
+
+        // Simulate an ACTION_MOVE event.
+        long event2Time = 1002;
+        MotionEvent event2 = moveEvent(event1Time, event2Time, moveX, moveY);
+        touchState.update(event2, mConfig);
+        String msg = String.format("(%.0f,%.0f)=>(%.0f,%.0f)", downX, downY, moveX, moveY);
+        assertThat(msg, touchState.getInitialDragDirectionXYRatio(),
+                is(expectedInitialDragDirectionXYRatio));
+    }
+
     private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
         return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
     }
@@ -441,7 +519,7 @@
     }
 
     private static void assertDrag(EditorTouchState touchState, float lastDownX,
-            float lastDownY, float lastUpX, float lastUpY, boolean isDragCloseToVertical) {
+            float lastDownY, float lastUpX, float lastUpY, float initialDragDirectionXYRatio) {
         assertThat(touchState.getLastDownX(), is(lastDownX));
         assertThat(touchState.getLastDownY(), is(lastDownY));
         assertThat(touchState.getLastUpX(), is(lastUpX));
@@ -451,7 +529,7 @@
         assertThat(touchState.isMultiTap(), is(false));
         assertThat(touchState.isMultiTapInSameArea(), is(false));
         assertThat(touchState.isMovedEnoughForDrag(), is(true));
-        assertThat(touchState.isDragCloseToVertical(), is(isDragCloseToVertical));
+        assertThat(touchState.getInitialDragDirectionXYRatio(), is(initialDragDirectionXYRatio));
     }
 
     private static void assertMultiTap(EditorTouchState touchState,
@@ -467,6 +545,5 @@
                 || multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
         assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea));
         assertThat(touchState.isMovedEnoughForDrag(), is(false));
-        assertThat(touchState.isDragCloseToVertical(), is(false));
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
index 3e67b8b..22c41f3 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java
@@ -38,7 +38,8 @@
 @SmallTest
 public class BatteryStatsBinderCallStatsTest extends TestCase {
 
-    private static final int TRANSACTION_CODE = 100;
+    private static final int TRANSACTION_CODE1 = 100;
+    private static final int TRANSACTION_CODE2 = 101;
 
     /**
      * Test BatteryStatsImpl.Uid.noteBinderCallStats.
@@ -53,23 +54,23 @@
 
         Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
         BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid,
-                MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
         stat1.incrementalCallCount = 21;
         stat1.recordedCallCount = 5;
         stat1.cpuTimeMicros = 1000;
         callStats.add(stat1);
 
-        bi.noteBinderCallStats(workSourceUid, 42, callStats);
+        bi.noteBinderCallStats(workSourceUid, 42, callStats, null);
 
         callStats.clear();
         BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
-                MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */);
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
         stat2.incrementalCallCount = 9;
         stat2.recordedCallCount = 8;
         stat2.cpuTimeMicros = 500;
         callStats.add(stat2);
 
-        bi.noteBinderCallStats(workSourceUid, 8, callStats);
+        bi.noteBinderCallStats(workSourceUid, 8, callStats, null);
 
         BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid);
         assertEquals(42 + 8, uid.getBinderCallCount());
@@ -85,9 +86,62 @@
         assertEquals(500, value.recordedCpuTimeMicros);
     }
 
+
+    @Test
+    public void testProportionalSystemServiceUsage_noStatsForSomeMethods() throws Exception {
+        final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+        int callingUid = Process.FIRST_APPLICATION_UID + 1;
+        int workSourceUid1 = Process.FIRST_APPLICATION_UID + 1;
+        int workSourceUid2 = Process.FIRST_APPLICATION_UID + 2;
+        int workSourceUid3 = Process.FIRST_APPLICATION_UID + 3;
+
+        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+        BinderCallsStats.CallStat stat1a = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
+        stat1a.incrementalCallCount = 10;
+        stat1a.recordedCallCount = 5;
+        stat1a.cpuTimeMicros = 1000;
+        callStats.add(stat1a);
+
+        BinderCallsStats.CallStat stat1b = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE2, true /*screenInteractive */);
+        stat1b.incrementalCallCount = 30;
+        stat1b.recordedCallCount = 15;
+        stat1b.cpuTimeMicros = 1500;
+        callStats.add(stat1b);
+
+        bi.noteBinderCallStats(workSourceUid1, 65, callStats, null);
+
+        // No recorded stats for some methods. Must use the global average.
+        callStats.clear();
+        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid,
+                MockBinder.class, TRANSACTION_CODE1, true /*screenInteractive */);
+        stat2.incrementalCallCount = 10;
+        callStats.add(stat2);
+
+        bi.noteBinderCallStats(workSourceUid2, 40, callStats, null);
+
+        // No stats for any calls. Must use the global average
+        callStats.clear();
+        bi.noteBinderCallStats(workSourceUid3, 50, callStats, null);
+
+        bi.updateSystemServiceCallStats();
+
+        double prop1 = bi.getUidStatsLocked(workSourceUid1).getProportionalSystemServiceUsage();
+        double prop2 = bi.getUidStatsLocked(workSourceUid2).getProportionalSystemServiceUsage();
+        double prop3 = bi.getUidStatsLocked(workSourceUid3).getProportionalSystemServiceUsage();
+
+        assertEquals(0.419, prop1, 0.01);
+        assertEquals(0.258, prop2, 0.01);
+        assertEquals(0.323, prop3, 0.01);
+        assertEquals(1.000, prop1 + prop2 + prop3, 0.01);
+    }
+
     private static class MockBinder extends Binder {
         public static String getDefaultTransactionName(int txCode) {
-            return txCode == TRANSACTION_CODE ? "testMethod" : "unknown";
+            return txCode == TRANSACTION_CODE1 ? "testMethod" : "unknown";
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index a5117a3..188ba9e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -768,8 +768,8 @@
 
         final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>();
         bcs.setCallStatsObserver(
-                (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll(
-                        callStats));
+                (workSourceUid, incrementalCallCount, callStats, binderThreadIds) ->
+                        callStatsList.addAll(callStats));
 
         Binder binder = new Binder();
 
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index bc0e0a4..75dd7fb 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -133,6 +133,12 @@
         return this;
     }
 
+    public MockBatteryStatsImpl setSystemServerCpuThreadReader(
+            SystemServerCpuThreadReader systemServerCpuThreadReader) {
+        mSystemServerCpuThreadReader = systemServerCpuThreadReader;
+        return this;
+    }
+
     public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) {
         mUserInfoProvider = provider;
         return this;
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
new file mode 100644
index 0000000..10ba548
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.os.FileUtils;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemServerCpuThreadReaderTest {
+    private File mProcDirectory;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        FileUtils.deleteContents(mProcDirectory);
+    }
+
+    @Test
+    public void testReaderDelta_firstTime() throws IOException {
+        int uid = 42;
+        setupDirectory(
+                mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                new int[]{42, 1, 2, 3},
+                new int[]{1000, 2000},
+                // Units are 10ms aka 10000Us
+                new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+
+        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
+                mProcDirectory.toPath(), uid);
+        reader.setBinderThreadNativeTids(new int[]{1, 3});
+        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+                reader.readDelta();
+        assertArrayEquals(new long[]{100 * 10000, 1100 * 10000},
+                systemServiceCpuThreadTimes.threadCpuTimesUs);
+        assertArrayEquals(new long[]{0, 600 * 10000},
+                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+    }
+
+    @Test
+    public void testReaderDelta_nextTime() throws IOException {
+        int uid = 42;
+        setupDirectory(
+                mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                new int[]{42, 1, 2, 3},
+                new int[]{1000, 2000},
+                new int[][]{{100, 200}, {0, 200}, {0, 300}, {0, 400}});
+
+        SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
+                mProcDirectory.toPath(), uid);
+        reader.setBinderThreadNativeTids(new int[]{1, 3});
+
+        // First time, populate "last" snapshot
+        reader.readDelta();
+
+        FileUtils.deleteContents(mProcDirectory);
+        setupDirectory(
+                mProcDirectory.toPath().resolve(String.valueOf(uid)),
+                new int[]{42, 1, 2, 3},
+                new int[]{1000, 2000},
+                new int[][]{{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}});
+
+        // Second time, get the actual delta
+        SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
+                reader.readDelta();
+
+        assertArrayEquals(new long[]{3100 * 10000, 2500 * 10000},
+                systemServiceCpuThreadTimes.threadCpuTimesUs);
+        assertArrayEquals(new long[]{1800 * 10000, 1400 * 10000},
+                systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+    }
+
+    private void setupDirectory(Path processPath, int[] threadIds, int[] cpuFrequencies,
+            int[][] cpuTimes) throws IOException {
+        // Make /proc/$PID
+        assertTrue(processPath.toFile().mkdirs());
+
+        // Make /proc/$PID/task
+        final Path selfThreadsPath = processPath.resolve("task");
+        assertTrue(selfThreadsPath.toFile().mkdirs());
+
+        // Make thread directories in reverse order, as they are read in order of creation by
+        // CpuThreadProcReader
+        for (int i = 0; i < threadIds.length; i++) {
+            // Make /proc/$PID/task/$TID
+            final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
+            assertTrue(threadPath.toFile().mkdirs());
+
+            // Make /proc/$PID/task/$TID/time_in_state
+            final OutputStream timeInStateStream =
+                    Files.newOutputStream(threadPath.resolve("time_in_state"));
+            for (int j = 0; j < cpuFrequencies.length; j++) {
+                final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
+                timeInStateStream.write(line.getBytes());
+            }
+            timeInStateStream.close();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
new file mode 100644
index 0000000..ac5443e
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+package com.android.internal.os;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Process;
+
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemServicePowerCalculatorTest {
+
+    private PowerProfile mProfile;
+    private MockBatteryStatsImpl mMockBatteryStats;
+    private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
+    private MockServerCpuThreadReader mMockServerCpuThreadReader;
+    private SystemServicePowerCalculator mSystemServicePowerCalculator;
+
+    @Before
+    public void setUp() throws IOException {
+        Context context = InstrumentationRegistry.getContext();
+        mProfile = new PowerProfile(context, true /* forTest */);
+        mMockServerCpuThreadReader = new MockServerCpuThreadReader();
+        mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
+        mMockBatteryStats = new MockBatteryStatsImpl(new MockClocks())
+                .setPowerProfile(mProfile)
+                .setSystemServerCpuThreadReader(mMockServerCpuThreadReader)
+                .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
+                .setUserInfoProvider(new MockUserInfoProvider());
+        mMockBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
+        mSystemServicePowerCalculator =
+                new SystemServicePowerCalculator(mProfile, mMockBatteryStats);
+    }
+
+    @Test
+    public void testCalculateApp() {
+        // Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
+        mMockServerCpuThreadReader.setThreadTimes(
+                new long[]{30000, 40000, 50000, 60000, 70000, 80000, 90000},
+                new long[]{20000, 30000, 40000, 50000, 60000, 70000, 80000});
+
+        mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
+                new long[]{10000, 20000, 30000, 40000, 50000, 60000, 70000}
+        );
+
+        mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false);
+
+        int workSourceUid1 = 100;
+        int workSourceUid2 = 200;
+        int transactionCode = 42;
+
+        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1,
+                Binder.class, transactionCode, true /*screenInteractive */);
+        stat1.incrementalCallCount = 100;
+        stat1.recordedCallCount = 100;
+        stat1.cpuTimeMicros = 1000000;
+        callStats.add(stat1);
+
+        mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats, null);
+
+        callStats.clear();
+        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
+                Binder.class, transactionCode, true /*screenInteractive */);
+        stat2.incrementalCallCount = 100;
+        stat2.recordedCallCount = 100;
+        stat2.cpuTimeMicros = 9000000;
+        callStats.add(stat2);
+
+        mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats, null);
+
+        mMockBatteryStats.updateSystemServiceCallStats();
+        mMockBatteryStats.updateSystemServerThreadStats();
+
+        BatterySipper app1 = new BatterySipper(BatterySipper.DrainType.APP,
+                mMockBatteryStats.getUidStatsLocked(workSourceUid1), 0);
+        mSystemServicePowerCalculator.calculateApp(app1, app1.uidObj, 0, 0,
+                BatteryStats.STATS_SINCE_CHARGED);
+        assertEquals(0.27162, app1.systemServiceCpuPowerMah, 0.00001);
+
+        BatterySipper app2 = new BatterySipper(BatterySipper.DrainType.APP,
+                mMockBatteryStats.getUidStatsLocked(workSourceUid2), 0);
+        mSystemServicePowerCalculator.calculateApp(app2, app2.uidObj, 0, 0,
+                BatteryStats.STATS_SINCE_CHARGED);
+        assertEquals(2.44458, app2.systemServiceCpuPowerMah, 0.00001);
+    }
+
+    private static class MockKernelCpuUidFreqTimeReader extends
+            KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader {
+        private long[] mSystemServerCpuTimes;
+
+        MockKernelCpuUidFreqTimeReader() {
+            super(/*throttle */false);
+        }
+
+        void setSystemServerCpuTimes(long[] systemServerCpuTimes) {
+            mSystemServerCpuTimes = systemServerCpuTimes;
+        }
+
+        @Override
+        public boolean perClusterTimesAvailable() {
+            return true;
+        }
+
+        @Override
+        public void readDelta(@Nullable Callback<long[]> cb) {
+            if (cb != null) {
+                cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
+            }
+        }
+    }
+
+    private static class MockServerCpuThreadReader extends SystemServerCpuThreadReader {
+        private SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
+
+        MockServerCpuThreadReader() {
+            super(null);
+        }
+
+        public void setThreadTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
+            mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
+            mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
+        }
+
+        @Override
+        public SystemServiceCpuThreadTimes readDelta() {
+            return mThreadTimes;
+        }
+    }
+
+    private static class MockUserInfoProvider extends BatteryStatsImpl.UserInfoProvider {
+        @Nullable
+        @Override
+        protected int[] getUserIds() {
+            return new int[0];
+        }
+
+        @Override
+        public boolean exists(int userId) {
+            return true;
+        }
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f834ce7..b3c8263 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -432,18 +432,6 @@
         <permission name="android.permission.CAPTURE_AUDIO_OUTPUT" />
         <!-- Permissions required for CTS test - AdbManagerTest -->
         <permission name="android.permission.MANAGE_DEBUGGING" />
-        <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
-        <permission name="android.car.permission.CAR_DRIVING_STATE" />
-        <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
-        <permission name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-        <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-        <permission name="android.car.permission.CAR_DIAGNOSTICS" />
-        <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-        <permission name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
-        <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-        <permission name="android.car.permission.CONTROL_APP_BLOCKING" />
-        <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-        <permission name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
index 3ffb8ea..a2ee065 100644
--- a/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
+++ b/errorprone/java/com/google/errorprone/bugpatterns/android/UidChecker.java
@@ -23,12 +23,15 @@
 import com.google.errorprone.VisitorState;
 import com.google.errorprone.bugpatterns.BugChecker;
 import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
+import com.google.errorprone.bugpatterns.BugChecker.NewClassTreeMatcher;
 import com.google.errorprone.matchers.Description;
 import com.google.errorprone.util.ASTHelpers;
 import com.sun.source.tree.ExpressionTree;
 import com.sun.source.tree.IdentifierTree;
 import com.sun.source.tree.MemberSelectTree;
 import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
 import com.sun.tools.javac.code.Symbol.VarSymbol;
 
 import java.util.List;
@@ -44,11 +47,20 @@
     name = "AndroidFrameworkUid",
     summary = "Verifies that PID, UID and user ID arguments aren't crossed",
     severity = WARNING)
-public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher {
+public final class UidChecker extends BugChecker implements MethodInvocationTreeMatcher,
+        NewClassTreeMatcher {
     @Override
     public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
-        final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params();
-        final List<? extends ExpressionTree> args = tree.getArguments();
+        return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree);
+    }
+
+    @Override
+    public Description matchNewClass(NewClassTree tree, VisitorState state) {
+        return matchArguments(ASTHelpers.getSymbol(tree).params(), tree.getArguments(), tree);
+    }
+
+    private Description matchArguments(List<VarSymbol> vars,
+            List<? extends ExpressionTree> args, Tree tree) {
         for (int i = 0; i < Math.min(vars.size(), args.size()); i++) {
             final Flavor varFlavor = getFlavor(vars.get(i));
             final Flavor argFlavor = getFlavor(args.get(i));
diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
index 74da947..75341fd 100644
--- a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
+++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/UidCheckerTest.java
@@ -34,7 +34,7 @@
     }
 
     @Test
-    public void testTypical() {
+    public void testTypical_methodInvocation() {
         compilationHelper
                 .addSourceLines("Example.java",
                         "public abstract class Example {",
@@ -57,7 +57,30 @@
     }
 
     @Test
-    public void testCallingUid() {
+    public void testTypical_newClass() {
+        compilationHelper
+                .addSourceLines("Example.java",
+                        "public abstract class Example {",
+                        "  class Bar { Bar(int pid, int uid, int userId) {} }",
+                        "  abstract int getUserId();",
+                        "  void foo(int pid, int uid, int userId, int unrelated) {",
+                        "    new Bar(0, 0, 0);",
+                        "    new Bar(pid, uid, userId);",
+                        "    new Bar(pid, uid, getUserId());",
+                        "    new Bar(unrelated, unrelated, unrelated);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(uid, pid, userId);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(pid, userId, uid);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(getUserId(), 0, 0);",
+                        "  }",
+                        "}")
+                .doTest();
+    }
+
+    @Test
+    public void testCallingUid_methodInvocation() {
         compilationHelper
                 .addSourceFile("/android/os/Binder.java")
                 .addSourceFile("/android/os/UserHandle.java")
@@ -98,4 +121,48 @@
                         "}")
                 .doTest();
     }
+
+
+    @Test
+    public void testCallingUid_newClass() {
+        compilationHelper
+                .addSourceFile("/android/os/Binder.java")
+                .addSourceFile("/android/os/UserHandle.java")
+                .addSourceLines("Example.java",
+                        "import android.os.Binder;",
+                        "import android.os.UserHandle;",
+                        "public abstract class Example {",
+                        "  int callingUserId;",
+                        "  int callingUid;",
+                        "  class Foo { Foo(int callingUserId) {} }",
+                        "  class Bar { Bar(int callingUid) {} }",
+                        "  void doUserId(int callingUserId) {",
+                        "    new Foo(UserHandle.getUserId(Binder.getCallingUid()));",
+                        "    new Foo(this.callingUserId);",
+                        "    new Foo(callingUserId);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(Binder.getCallingUid());",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(this.callingUid);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(callingUid);",
+                        "  }",
+                        "  void doUid(int callingUserId) {",
+                        "    new Bar(Binder.getCallingUid());",
+                        "    new Bar(this.callingUid);",
+                        "    new Bar(callingUid);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(UserHandle.getUserId(Binder.getCallingUid()));",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(this.callingUserId);",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Bar(callingUserId);",
+                        "  }",
+                        "  void doInner() {",
+                        "    // BUG: Diagnostic contains:",
+                        "    new Foo(UserHandle.getUserId(callingUserId));",
+                        "  }",
+                        "}")
+                .doTest();
+    }
 }
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index 54618a5..70ef69d 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11900,6 +11900,7 @@
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
+    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
index 5dcb9de..a2cd044 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/sideloaded/SideLoadedAppDetector.java
@@ -39,7 +39,7 @@
 
 /**
  * A class that detects unsafe apps.
- * An app is considered safe if is a system app or installed through whitelisted sources.
+ * An app is considered safe if is a system app or installed through allowed sources.
  */
 @Singleton
 public class SideLoadedAppDetector {
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
index 31f1170..f77294e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/voicerecognition/ConnectedDeviceVoiceRecognitionNotifierTest.java
@@ -19,6 +19,8 @@
 import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.INVALID_VALUE;
 import static com.android.systemui.car.voicerecognition.ConnectedDeviceVoiceRecognitionNotifier.VOICE_RECOGNITION_STARTED;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -40,11 +42,13 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 
 @CarSystemUiTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 @SmallTest
+// TODO(b/162866441): Refactor to use the Executor pattern instead.
 public class ConnectedDeviceVoiceRecognitionNotifierTest extends SysuiTestCase {
 
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -52,13 +56,15 @@
 
     private ConnectedDeviceVoiceRecognitionNotifier mVoiceRecognitionNotifier;
     private TestableLooper mTestableLooper;
+    private Handler mHandler;
     private Handler mTestHandler;
     private BluetoothDevice mBluetoothDevice;
 
     @Before
     public void setUp() throws Exception {
         mTestableLooper = TestableLooper.get(this);
-        mTestHandler = spy(new Handler(mTestableLooper.getLooper()));
+        mHandler = new Handler(mTestableLooper.getLooper());
+        mTestHandler = spy(mHandler);
         mBluetoothDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
                 BLUETOOTH_REMOTE_ADDRESS);
         mVoiceRecognitionNotifier = new ConnectedDeviceVoiceRecognitionNotifier(
@@ -74,8 +80,14 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler).post(any());
+        mHandler.post(() -> {
+            ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
+            verify(mTestHandler).post(argumentCaptor.capture());
+            assertThat(argumentCaptor.getValue()).isNotNull();
+            assertThat(argumentCaptor.getValue()).isNotEqualTo(this);
+        });
     }
 
     @Test
@@ -86,8 +98,11 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 
     @Test
@@ -97,8 +112,11 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 
     @Test
@@ -108,7 +126,10 @@
 
         mContext.sendBroadcast(intent, BLUETOOTH_PERM);
         mTestableLooper.processAllMessages();
+        waitForIdleSync();
 
-        verify(mTestHandler, never()).post(any());
+        mHandler.post(() -> {
+            verify(mTestHandler, never()).post(any());
+        });
     }
 }
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
similarity index 87%
rename from packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java
rename to packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
index a12aa83..a08f566 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/MasterSwitchController.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/PrimarySwitchController.java
@@ -19,9 +19,9 @@
 import android.os.Bundle;
 
 /**
- * A controller that manages event for master switch.
+ * A controller that manages event for Primary switch.
  */
-public abstract class MasterSwitchController extends SwitchController {
+public abstract class PrimarySwitchController extends SwitchController {
 
     @Override
     protected final MetaData getMetaData() {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
index 73f1a90..f2b3e30 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/SwitchesProvider.java
@@ -88,7 +88,7 @@
 
             controller.setAuthority(mAuthority);
             mControllerMap.put(key, controller);
-            if (!(controller instanceof MasterSwitchController)) {
+            if (!(controller instanceof PrimarySwitchController)) {
                 mSwitchDataList.add(controller.getBundle());
             }
         });
@@ -116,7 +116,7 @@
 
         switch (method) {
             case METHOD_GET_SWITCH_DATA:
-                if (!(controller instanceof MasterSwitchController)) {
+                if (!(controller instanceof PrimarySwitchController)) {
                     return controller.getBundle();
                 }
                 break;
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
index f757aa4..b29595e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -24,7 +24,7 @@
 import com.android.settingslib.core.AbstractPreferenceController;
 
 /**
- * This controller is used handle changes for the master switch in the developer options page.
+ * This controller is used handle changes for the primary switch in the developer options page.
  *
  * All Preference Controllers that are a part of the developer options page should inherit this
  * class.
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
similarity index 69%
rename from packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
rename to packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
index 3c647a7..c501b3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerWhitelistBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/PowerAllowlistBackend.java
@@ -34,44 +34,50 @@
 import com.android.internal.util.ArrayUtils;
 
 /**
- * Handles getting/changing the whitelist for the exceptions to battery saving features.
+ * Handles getting/changing the allowlist for the exceptions to battery saving features.
  */
-public class PowerWhitelistBackend {
+public class PowerAllowlistBackend {
 
-    private static final String TAG = "PowerWhitelistBackend";
+    private static final String TAG = "PowerAllowlistBackend";
 
     private static final String DEVICE_IDLE_SERVICE = "deviceidle";
 
-    private static PowerWhitelistBackend sInstance;
+    private static PowerAllowlistBackend sInstance;
 
     private final Context mAppContext;
     private final IDeviceIdleController mDeviceIdleService;
-    private final ArraySet<String> mWhitelistedApps = new ArraySet<>();
-    private final ArraySet<String> mSysWhitelistedApps = new ArraySet<>();
+    private final ArraySet<String> mAllowlistedApps = new ArraySet<>();
+    private final ArraySet<String> mSysAllowlistedApps = new ArraySet<>();
     private final ArraySet<String> mDefaultActiveApps = new ArraySet<>();
 
-    public PowerWhitelistBackend(Context context) {
+    public PowerAllowlistBackend(Context context) {
         this(context, IDeviceIdleController.Stub.asInterface(
                 ServiceManager.getService(DEVICE_IDLE_SERVICE)));
     }
 
     @VisibleForTesting
-    PowerWhitelistBackend(Context context, IDeviceIdleController deviceIdleService) {
+    PowerAllowlistBackend(Context context, IDeviceIdleController deviceIdleService) {
         mAppContext = context.getApplicationContext();
         mDeviceIdleService = deviceIdleService;
         refreshList();
     }
 
-    public int getWhitelistSize() {
-        return mWhitelistedApps.size();
+    public int getAllowlistSize() {
+        return mAllowlistedApps.size();
     }
 
-    public boolean isSysWhitelisted(String pkg) {
-        return mSysWhitelistedApps.contains(pkg);
+    /**
+    * Check if target package is in System allow list
+    */
+    public boolean isSysAllowlisted(String pkg) {
+        return mSysAllowlistedApps.contains(pkg);
     }
 
-    public boolean isWhitelisted(String pkg) {
-        if (mWhitelistedApps.contains(pkg)) {
+    /**
+     * Check if target package is in allow list
+     */
+    public boolean isAllowlisted(String pkg) {
+        if (mAllowlistedApps.contains(pkg)) {
             return true;
         }
 
@@ -87,7 +93,7 @@
      */
     public boolean isDefaultActiveApp(String pkg) {
         // Additionally, check if pkg is default dialer/sms. They are considered essential apps and
-        // should be automatically whitelisted (otherwise user may be able to set restriction on
+        // should be automatically allowlisted (otherwise user may be able to set restriction on
         // them, leading to bad device behavior.)
 
         if (mDefaultActiveApps.contains(pkg)) {
@@ -103,12 +109,17 @@
         return false;
     }
 
-    public boolean isWhitelisted(String[] pkgs) {
+    /**
+     *
+     * @param pkgs a list of packageName
+     * @return true when one of package is in allow list
+     */
+    public boolean isAllowlisted(String[] pkgs) {
         if (ArrayUtils.isEmpty(pkgs)) {
             return false;
         }
         for (String pkg : pkgs) {
-            if (isWhitelisted(pkg)) {
+            if (isAllowlisted(pkg)) {
                 return true;
             }
         }
@@ -116,40 +127,51 @@
         return false;
     }
 
+    /**
+     * Add app into power save allow list.
+     * @param pkg packageName
+     */
     public void addApp(String pkg) {
         try {
             mDeviceIdleService.addPowerSaveWhitelistApp(pkg);
-            mWhitelistedApps.add(pkg);
+            mAllowlistedApps.add(pkg);
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         }
     }
 
+    /**
+     * Remove package from power save allow list.
+     * @param pkg
+     */
     public void removeApp(String pkg) {
         try {
             mDeviceIdleService.removePowerSaveWhitelistApp(pkg);
-            mWhitelistedApps.remove(pkg);
+            mAllowlistedApps.remove(pkg);
         } catch (RemoteException e) {
             Log.w(TAG, "Unable to reach IDeviceIdleController", e);
         }
     }
 
+    /**
+     * Refresh all of lists
+     */
     @VisibleForTesting
     public void refreshList() {
-        mSysWhitelistedApps.clear();
-        mWhitelistedApps.clear();
+        mSysAllowlistedApps.clear();
+        mAllowlistedApps.clear();
         mDefaultActiveApps.clear();
         if (mDeviceIdleService == null) {
             return;
         }
         try {
-            final String[] whitelistedApps = mDeviceIdleService.getFullPowerWhitelist();
-            for (String app : whitelistedApps) {
-                mWhitelistedApps.add(app);
+            final String[] allowlistedApps = mDeviceIdleService.getFullPowerWhitelist();
+            for (String app : allowlistedApps) {
+                mAllowlistedApps.add(app);
             }
-            final String[] sysWhitelistedApps = mDeviceIdleService.getSystemPowerWhitelist();
-            for (String app : sysWhitelistedApps) {
-                mSysWhitelistedApps.add(app);
+            final String[] sysAllowlistedApps = mDeviceIdleService.getSystemPowerWhitelist();
+            for (String app : sysAllowlistedApps) {
+                mSysAllowlistedApps.add(app);
             }
             final boolean hasTelephony = mAppContext.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_TELEPHONY);
@@ -171,9 +193,13 @@
         }
     }
 
-    public static PowerWhitelistBackend getInstance(Context context) {
+    /**
+     * @param context
+     * @return a PowerAllowlistBackend object
+     */
+    public static PowerAllowlistBackend getInstance(Context context) {
         if (sInstance == null) {
-            sInstance = new PowerWhitelistBackend(context);
+            sInstance = new PowerAllowlistBackend(context);
         }
         return sInstance;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
index 4941f7e..8ac4349 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AppRestrictionsHelper.java
@@ -135,7 +135,7 @@
                 // Ignore
             }
         } else {
-            // Blacklist all other apps, system or downloaded
+            // Denylist all other apps, system or downloaded
             try {
                 ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
                 if (info != null) {
@@ -258,11 +258,11 @@
             }
         }
 
-        // Establish master/slave relationship for entries that share a package name
+        // Establish primary/secondary relationship for entries that share a package name
         HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
         for (SelectableAppInfo info : mVisibleApps) {
             if (packageMap.containsKey(info.packageName)) {
-                info.masterEntry = packageMap.get(info.packageName);
+                info.primaryEntry = packageMap.get(info.packageName);
             } else {
                 packageMap.put(info.packageName, info);
             }
@@ -366,12 +366,12 @@
         public CharSequence appName;
         public CharSequence activityName;
         public Drawable icon;
-        public SelectableAppInfo masterEntry;
+        public SelectableAppInfo primaryEntry;
 
         @Override
         public String toString() {
             return packageName + ": appName=" + appName + "; activityName=" + activityName
-                    + "; icon=" + icon + "; masterEntry=" + masterEntry;
+                    + "; icon=" + icon + "; primaryEntry=" + primaryEntry;
         }
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 11c799e..94e28f2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -290,12 +290,12 @@
     @Test
     public void sendShowAdminSupportDetailsIntent_extraRestrictionProvided() {
         EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
-        enforcedAdmin.enforcedRestriction = "Dummy";
+        enforcedAdmin.enforcedRestriction = "Fake";
         RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, enforcedAdmin);
 
         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
         verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
-        assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Dummy");
+        assertThat(intentCaptor.getValue().getExtra(EXTRA_RESTRICTION)).isEqualTo("Fake");
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
similarity index 86%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
index 69d0f2e..9e4cde8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/MasterSwitchControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/PrimarySwitchControllerTest.java
@@ -23,16 +23,16 @@
 import org.robolectric.RobolectricTestRunner;
 
 @RunWith(RobolectricTestRunner.class)
-public class MasterSwitchControllerTest {
+public class PrimarySwitchControllerTest {
 
     @Rule
     public final ExpectedException thrown = ExpectedException.none();
 
-    private MasterSwitchController mController;
+    private PrimarySwitchController mController;
 
     @Before
     public void setUp() {
-        mController = new TestMasterSwitchController("123");
+        mController = new TestPrimarySwitchController("123");
     }
 
     @Test
@@ -49,11 +49,11 @@
         mController.getBundle();
     }
 
-    static class TestMasterSwitchController extends MasterSwitchController {
+    static class TestPrimarySwitchController extends PrimarySwitchController {
 
         private String mKey;
 
-        TestMasterSwitchController(String key) {
+        TestPrimarySwitchController(String key) {
             mKey = key;
         }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
index a740e68..bd0100b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/SwitchesProviderTest.java
@@ -35,7 +35,7 @@
 import android.content.pm.ProviderInfo;
 import android.os.Bundle;
 
-import com.android.settingslib.drawer.MasterSwitchControllerTest.TestMasterSwitchController;
+import com.android.settingslib.drawer.PrimarySwitchControllerTest.TestPrimarySwitchController;
 import com.android.settingslib.drawer.SwitchController.MetaData;
 
 import org.junit.Before;
@@ -124,8 +124,8 @@
     }
 
     @Test
-    public void getSwitchData_shouldNotReturnMasterSwitchData() {
-        final SwitchController controller = new TestMasterSwitchController("123");
+    public void getSwitchData_shouldNotReturnPrimarySwitchData() {
+        final SwitchController controller = new TestPrimarySwitchController("123");
         mSwitchesProvider.addSwitchController(controller);
         mSwitchesProvider.attachInfo(mContext, mProviderInfo);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
similarity index 67%
rename from packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
rename to packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
index 2090892..4f11fb1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerWhitelistBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/fuelgauge/PowerAllowlistBackendTest.java
@@ -47,7 +47,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowDefaultDialerManager.class, ShadowSmsApplication.class})
-public class PowerWhitelistBackendTest {
+public class PowerAllowlistBackendTest {
 
     private static final String PACKAGE_ONE = "com.example.packageone";
     private static final String PACKAGE_TWO = "com.example.packagetwo";
@@ -56,7 +56,7 @@
     private IDeviceIdleController mDeviceIdleService;
     @Mock
     private DevicePolicyManager mDevicePolicyManager;
-    private PowerWhitelistBackend mPowerWhitelistBackend;
+    private PowerAllowlistBackend mPowerAllowlistBackend;
     private ShadowPackageManager mPackageManager;
     private Context mContext;
 
@@ -74,81 +74,81 @@
         mPackageManager.setSystemFeature(PackageManager.FEATURE_TELEPHONY, true);
         doReturn(mDevicePolicyManager).when(mContext).getSystemService(DevicePolicyManager.class);
 
-        mPowerWhitelistBackend = new PowerWhitelistBackend(mContext, mDeviceIdleService);
+        mPowerAllowlistBackend = new PowerAllowlistBackend(mContext, mDeviceIdleService);
     }
 
     @Test
-    public void testIsWhitelisted() throws Exception {
+    public void testIsAllowlisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getFullPowerWhitelist();
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
 
-        mPowerWhitelistBackend.addApp(PACKAGE_TWO);
+        mPowerAllowlistBackend.addApp(PACKAGE_TWO);
 
         verify(mDeviceIdleService, atLeastOnce()).addPowerSaveWhitelistApp(PACKAGE_TWO);
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(
                 new String[] {PACKAGE_ONE, PACKAGE_TWO})).isTrue();
 
-        mPowerWhitelistBackend.removeApp(PACKAGE_TWO);
+        mPowerAllowlistBackend.removeApp(PACKAGE_TWO);
 
         verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_TWO);
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_ONE})).isTrue();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(new String[] {PACKAGE_TWO})).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_ONE})).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(new String[] {PACKAGE_TWO})).isFalse();
 
-        mPowerWhitelistBackend.removeApp(PACKAGE_ONE);
+        mPowerAllowlistBackend.removeApp(PACKAGE_ONE);
 
         verify(mDeviceIdleService, atLeastOnce()).removePowerSaveWhitelistApp(PACKAGE_ONE);
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(
                 new String[] {PACKAGE_ONE, PACKAGE_TWO})).isFalse();
     }
 
     @Test
-    public void isWhitelisted_shouldWhitelistDefaultSms() {
+    public void isAllowlisted_shouldAllowlistDefaultSms() {
         final String testSms = "com.android.test.defaultsms";
         ShadowSmsApplication.setDefaultSmsApplication(new ComponentName(testSms, "receiver"));
 
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(testSms)).isTrue();
-        assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testSms)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(testSms)).isTrue();
+        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testSms)).isTrue();
     }
 
     @Test
-    public void isWhitelisted_shouldWhitelistDefaultDialer() {
+    public void isAllowlisted_shouldAllowlistDefaultDialer() {
         final String testDialer = "com.android.test.defaultdialer";
         ShadowDefaultDialerManager.setDefaultDialerApplication(testDialer);
 
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(testDialer)).isTrue();
-        assertThat(mPowerWhitelistBackend.isDefaultActiveApp(testDialer)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(testDialer)).isTrue();
+        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(testDialer)).isTrue();
     }
 
     @Test
-    public void isWhitelisted_shouldWhitelistActiveDeviceAdminApp() {
+    public void isAllowlisted_shouldAllowlistActiveDeviceAdminApp() {
         doReturn(true).when(mDevicePolicyManager).packageHasActiveAdmins(PACKAGE_ONE);
 
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isDefaultActiveApp(PACKAGE_ONE)).isTrue();
     }
 
     @Test
-    public void testIsSystemWhitelisted() throws Exception {
+    public void testIsSystemAllowlisted() throws Exception {
         doReturn(new String[] {PACKAGE_ONE}).when(mDeviceIdleService).getSystemPowerWhitelist();
-        mPowerWhitelistBackend.refreshList();
+        mPowerAllowlistBackend.refreshList();
 
-        assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_ONE)).isTrue();
-        assertThat(mPowerWhitelistBackend.isSysWhitelisted(PACKAGE_TWO)).isFalse();
-        assertThat(mPowerWhitelistBackend.isWhitelisted(PACKAGE_ONE)).isFalse();
+        assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_ONE)).isTrue();
+        assertThat(mPowerAllowlistBackend.isSysAllowlisted(PACKAGE_TWO)).isFalse();
+        assertThat(mPowerAllowlistBackend.isAllowlisted(PACKAGE_ONE)).isFalse();
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
index b930aa6..84d722a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilCompatTest.java
@@ -197,61 +197,61 @@
     public void isValidSystemNonAuxAsciiCapableIme() {
         // System IME w/ no subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false)))
+                createFakeIme(true, false)))
                 .isFalse();
 
         // System IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false, createDummySubtype("keyboard", false, false))))
+                createFakeIme(true, false, createFakeSubtype("keyboard", false, false))))
                 .isFalse();
 
         // System IME w/ non-Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false, createDummySubtype("keyboard", false, true))))
+                createFakeIme(true, false, createFakeSubtype("keyboard", false, true))))
                 .isTrue();
 
         // System IME w/ Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, true, createDummySubtype("keyboard", true, true))))
+                createFakeIme(true, true, createFakeSubtype("keyboard", true, true))))
                 .isFalse();
 
         // System IME w/ non-Aux and ASCII-capable "voice" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false, createDummySubtype("voice", false, true))))
+                createFakeIme(true, false, createFakeSubtype("voice", false, true))))
                 .isFalse();
 
         // System IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(true, false,
-                        createDummySubtype("keyboard", false, true),
-                        createDummySubtype("keyboard", false, false))))
+                createFakeIme(true, false,
+                        createFakeSubtype("keyboard", false, true),
+                        createFakeSubtype("keyboard", false, false))))
                 .isTrue();
 
         // Non-system IME w/ non-Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtilCompat.isValidSystemNonAuxAsciiCapableIme(
-                createDummyIme(false, false, createDummySubtype("keyboard", false, true))))
+                createFakeIme(false, false, createFakeSubtype("keyboard", false, true))))
                 .isFalse();
     }
 
-    private static InputMethodInfo createDummyIme(boolean isSystem, boolean isAuxIme,
+    private static InputMethodInfo createFakeIme(boolean isSystem, boolean isAuxIme,
             InputMethodSubtype... subtypes) {
         final ResolveInfo ri = new ResolveInfo();
         final ServiceInfo si = new ServiceInfo();
         final ApplicationInfo ai = new ApplicationInfo();
-        ai.packageName = "com.example.android.dummyime";
+        ai.packageName = "com.example.android.fakeime";
         ai.enabled = true;
         ai.flags |= (isSystem ? ApplicationInfo.FLAG_SYSTEM : 0);
         si.applicationInfo = ai;
         si.enabled = true;
-        si.packageName = "com.example.android.dummyime";
-        si.name = "Dummy IME";
+        si.packageName = "com.example.android.fakeime";
+        si.name = "Fake IME";
         si.exported = true;
-        si.nonLocalizedLabel = "Dummy IME";
+        si.nonLocalizedLabel = "Fake IME";
         ri.serviceInfo = si;
         return new InputMethodInfo(ri, isAuxIme, "", Arrays.asList(subtypes), 1, false);
     }
 
-    private static InputMethodSubtype createDummySubtype(
+    private static InputMethodSubtype createFakeSubtype(
             String mode, boolean isAuxiliary, boolean isAsciiCapable) {
         return new InputMethodSubtypeBuilder()
                 .setSubtypeNameResId(0)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
index 5171dda..97d8705 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -195,55 +195,55 @@
     public void isValidNonAuxAsciiCapableIme() {
         // IME w/ no subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false)))
+                createFakeIme(false)))
                 .isFalse();
 
         // IME w/ non-Aux and non-ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false, createDummySubtype("keyboard", false, false))))
+                createFakeIme(false, createFakeSubtype("keyboard", false, false))))
                 .isFalse();
 
         // IME w/ non-Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false, createDummySubtype("keyboard", false, true))))
+                createFakeIme(false, createFakeSubtype("keyboard", false, true))))
                 .isTrue();
 
         // IME w/ Aux and ASCII-capable "keyboard" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(true, createDummySubtype("keyboard", true, true))))
+                createFakeIme(true, createFakeSubtype("keyboard", true, true))))
                 .isFalse();
 
         // IME w/ non-Aux and ASCII-capable "voice" subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false, createDummySubtype("voice", false, true))))
+                createFakeIme(false, createFakeSubtype("voice", false, true))))
                 .isFalse();
 
         // IME w/ non-Aux and non-ASCII-capable subtype + Non-Aux and ASCII-capable subtype
         assertThat(InputMethodAndSubtypeUtil.isValidNonAuxAsciiCapableIme(
-                createDummyIme(false,
-                        createDummySubtype("keyboard", false, true),
-                        createDummySubtype("keyboard", false, false))))
+                createFakeIme(false,
+                        createFakeSubtype("keyboard", false, true),
+                        createFakeSubtype("keyboard", false, false))))
                 .isTrue();
    }
 
-    private static InputMethodInfo createDummyIme(boolean isAuxIme,
+    private static InputMethodInfo createFakeIme(boolean isAuxIme,
             InputMethodSubtype... subtypes) {
         final ResolveInfo ri = new ResolveInfo();
         final ServiceInfo si = new ServiceInfo();
         final ApplicationInfo ai = new ApplicationInfo();
-        ai.packageName = "com.example.android.dummyime";
+        ai.packageName = "com.example.android.fakeime";
         ai.enabled = true;
         si.applicationInfo = ai;
         si.enabled = true;
-        si.packageName = "com.example.android.dummyime";
-        si.name = "Dummy IME";
+        si.packageName = "com.example.android.fakeime";
+        si.name = "Fake IME";
         si.exported = true;
-        si.nonLocalizedLabel = "Dummy IME";
+        si.nonLocalizedLabel = "Fake IME";
         ri.serviceInfo = si;
         return new InputMethodInfo(ri, isAuxIme, "",  Arrays.asList(subtypes), 1, false);
     }
 
-    private static InputMethodSubtype createDummySubtype(
+    private static InputMethodSubtype createFakeSubtype(
             String mode, boolean isAuxiliary, boolean isAsciiCapable) {
         return new InputMethodSubtypeBuilder()
                 .setSubtypeNameResId(0)
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index aa96087..319b44c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -320,19 +320,6 @@
     <!-- Permissions required for CTS test - AdbManagerTest -->
     <uses-permission android:name="android.permission.MANAGE_DEBUGGING" />
 
-    <!-- Permissions required for ATS tests - AtsCarHostTestCases, AtsCarDeviceApp -->
-    <uses-permission android:name="android.car.permission.CAR_DRIVING_STATE" />
-    <!-- Permissions required for ATS tests - AtsDeviceInfo, AtsAudioDeviceTestCases -->
-    <uses-permission android:name="android.car.permission.CAR_CONTROL_AUDIO_VOLUME" />
-    <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-    <uses-permission android:name="android.car.permission.CAR_DIAGNOSTICS" />
-    <!-- Permissions required for ATS tests - AtsDeviceInfo -->
-    <uses-permission android:name="android.car.permission.CLEAR_CAR_DIAGNOSTICS" />
-    <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-    <uses-permission android:name="android.car.permission.CONTROL_APP_BLOCKING" />
-    <!-- Permissions required for ATS tests - AtsCarHostTestCases -->
-    <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION" />
-
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SimAppDialog/Android.bp b/packages/SimAppDialog/Android.bp
index 176035f..1c680bb 100644
--- a/packages/SimAppDialog/Android.bp
+++ b/packages/SimAppDialog/Android.bp
@@ -7,7 +7,8 @@
 
     static_libs: [
         "androidx.legacy_legacy-support-v4",
-        "setup-wizard-lib",
+        "setupcompat",
+        "setupdesign",
     ],
 
     resource_dirs: ["res"],
diff --git a/packages/SimAppDialog/AndroidManifest.xml b/packages/SimAppDialog/AndroidManifest.xml
index 873f6c5..e7368f3 100644
--- a/packages/SimAppDialog/AndroidManifest.xml
+++ b/packages/SimAppDialog/AndroidManifest.xml
@@ -23,7 +23,7 @@
             android:name=".InstallCarrierAppActivity"
             android:exported="true"
             android:permission="android.permission.NETWORK_SETTINGS"
-            android:theme="@style/SuwThemeGlif.Light">
+            android:theme="@style/SudThemeGlif.Light">
         </activity>
     </application>
 </manifest>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
index 12f9bb6..68113db 100644
--- a/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
+++ b/packages/SimAppDialog/res/layout/install_carrier_app_activity.xml
@@ -14,18 +14,17 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.android.setupwizardlib.GlifLayout
+<com.google.android.setupdesign.GlifLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/setup_wizard_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:icon="@drawable/ic_signal_cellular_alt_rounded"
-    app:suwHeaderText="@string/install_carrier_app_title"
-    app:suwFooter="@layout/install_carrier_app_footer">
+    app:sucHeaderText="@string/install_carrier_app_title">
 
     <LinearLayout
-        style="@style/SuwContentFrame"
+        style="@style/SudContentFrame"
         android:id="@+id/content_frame"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -33,12 +32,12 @@
 
         <TextView
             android:id="@+id/install_carrier_app_description"
-            style="@style/SuwDescription.Glif"
+            style="@style/SudDescription.Glif"
             android:text="@string/install_carrier_app_description_default"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"/>
 
-        <com.android.setupwizardlib.view.FillContentLayout
+        <com.google.android.setupdesign.view.FillContentLayout
             android:id="@+id/illo_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -47,12 +46,12 @@
 
             <ImageView
                 android:src="@drawable/illo_sim_app_dialog"
-                style="@style/SuwContentIllustration"
+                style="@style/SudContentIllustration"
                 android:contentDescription="@string/install_carrier_app_image_content_description"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"/>
 
-        </com.android.setupwizardlib.view.FillContentLayout>
-</LinearLayout>
+        </com.google.android.setupdesign.view.FillContentLayout>
+    </LinearLayout>
 
-</com.android.setupwizardlib.GlifLayout>
+</com.google.android.setupdesign.GlifLayout>
diff --git a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml b/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
deleted file mode 100644
index 10dcb77..0000000
--- a/packages/SimAppDialog/res/layout/install_carrier_app_footer.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2018 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-
-<com.android.setupwizardlib.view.ButtonBarLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/footer"
-    style="@style/SuwGlifButtonBar.Stackable"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <Button
-        android:id="@+id/skip_button"
-        style="@style/SuwGlifButton.Secondary"
-        android:text="@string/install_carrier_app_defer_action"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"/>
-
-    <Button
-        android:id="@+id/download_button"
-        style="@style/SuwGlifButton.Primary"
-        android:text="@string/install_carrier_app_download_action"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"/>
-</com.android.setupwizardlib.view.ButtonBarLayout>
diff --git a/packages/SimAppDialog/res/values/styles.xml b/packages/SimAppDialog/res/values/styles.xml
new file mode 100644
index 0000000..824e380
--- /dev/null
+++ b/packages/SimAppDialog/res/values/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    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.
+-->
+<resources>
+
+  <style name="SetupWizardPartnerResource">
+    <!-- Disable to use partner overlay theme for outside setupwizard flow. -->
+    <item name="sucUsePartnerResource">false</item>
+    <!-- Enable heavy theme style inside setupwizard flow. -->
+    <item name="sudUsePartnerHeavyTheme">true</item>
+  </style>
+
+</resources>
diff --git a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
index abe82a8..0b6f9bb 100644
--- a/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
+++ b/packages/SimAppDialog/src/com/android/simappdialog/InstallCarrierAppActivity.java
@@ -17,14 +17,17 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.Bundle;
 import android.sysprop.SetupWizardProperties;
 import android.text.TextUtils;
 import android.view.View;
-import android.widget.Button;
 import android.widget.TextView;
 
-import com.android.setupwizardlib.util.WizardManagerHelper;
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.template.FooterButton;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.util.ThemeResolver;
 
 /**
  * Activity that gives a user the choice to download the SIM app or defer until a later time
@@ -35,7 +38,7 @@
  * Can display the carrier app name if its passed into the intent with key
  * {@link #BUNDLE_KEY_CARRIER_NAME}
  */
-public class InstallCarrierAppActivity extends Activity implements View.OnClickListener {
+public class InstallCarrierAppActivity extends Activity {
     /**
      * Key for the carrier app name that will be displayed as the app to download.  If unset, a
      * default description will be used
@@ -50,20 +53,33 @@
     protected void onCreate(Bundle icicle) {
         // Setup theme for aosp/pixel
         setTheme(
-                WizardManagerHelper.getThemeRes(
-                        SetupWizardProperties.theme().orElse(""),
-                        R.style.SuwThemeGlif_Light
-                )
-        );
+                new ThemeResolver.Builder()
+                        .setDefaultTheme(R.style.SudThemeGlifV3_Light)
+                        .build()
+                        .resolve(SetupWizardProperties.theme().orElse(""),
+                                /* suppressDayNight= */ false));
 
         super.onCreate(icicle);
         setContentView(R.layout.install_carrier_app_activity);
 
-        Button notNowButton = findViewById(R.id.skip_button);
-        notNowButton.setOnClickListener(this);
+        GlifLayout layout = findViewById(R.id.setup_wizard_layout);
+        FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class);
+        mixin.setSecondaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.install_carrier_app_defer_action)
+                        .setListener(this::onSkipButtonClick)
+                        .setButtonType(FooterButton.ButtonType.SKIP)
+                        .setTheme(R.style.SudGlifButton_Secondary)
+                        .build());
 
-        Button downloadButton = findViewById(R.id.download_button);
-        downloadButton.setOnClickListener(this);
+        mixin.setPrimaryButton(
+                new FooterButton.Builder(this)
+                        .setText(R.string.install_carrier_app_download_action)
+                        .setListener(this::onDownloadButtonClick)
+                        .setButtonType(FooterButton.ButtonType.OTHER)
+                        .setTheme(R.style.SudGlifButton_Primary)
+                        .build());
+
 
         // Show/hide illo depending on whether one was provided in a resource overlay
         boolean showIllo = getResources().getBoolean(R.bool.show_sim_app_dialog_illo);
@@ -82,15 +98,17 @@
     }
 
     @Override
-    public void onClick(View v) {
-        switch (v.getId()) {
-            case R.id.skip_button:
-                finish(DEFER_RESULT);
-                break;
-            case R.id.download_button:
-                finish(DOWNLOAD_RESULT);
-                break;
-        }
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        theme.applyStyle(R.style.SetupWizardPartnerResource, true);
+        super.onApplyThemeResource(theme, resid, first);
+    }
+
+    protected void onSkipButtonClick(View view) {
+        finish(DEFER_RESULT);
+    }
+
+    protected void onDownloadButtonClick(View view) {
+        finish(DOWNLOAD_RESULT);
     }
 
     private void finish(int resultCode) {
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fa620df..fba43a6 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -515,13 +515,11 @@
     <!-- Whether or not to add a "people" notifications section -->
     <bool name="config_usePeopleFiltering">false</bool>
 
-    <!-- Defines the blacklist for system icons.  That is to say, the icons in the status bar that
-         are part of the blacklist are never displayed. Each item in the blacklist must be a string
-         defined in core/res/res/config.xml to properly blacklist the icon.
-
-         TODO: See if we can rename this config variable.
+    <!-- Defines system icons to be excluded from the display. That is to say, the icons in the
+         status bar that are part of this list are never displayed. Each item in the list must be a
+         string defined in core/res/res/config.xml to properly exclude the icon.
      -->
-    <string-array name="config_statusBarIconBlackList" translatable="false">
+    <string-array name="config_statusBarIconsToExclude" translatable="false">
         <item>@*android:string/status_bar_rotate</item>
         <item>@*android:string/status_bar_headset</item>
     </string-array>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index 816bcf8..d5f74a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -53,8 +53,8 @@
             ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION;
 
     @VisibleForTesting
-    protected WindowMagnificationController mWindowMagnificationController;
-    protected final ModeSwitchesController mModeSwitchesController;
+    protected WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+    private final ModeSwitchesController mModeSwitchesController;
     private final Handler mHandler;
     private final AccessibilityManager mAccessibilityManager;
     private final CommandQueue mCommandQueue;
@@ -72,6 +72,11 @@
                 Context.ACCESSIBILITY_SERVICE);
         mCommandQueue = commandQueue;
         mModeSwitchesController = modeSwitchesController;
+        final WindowMagnificationController controller = new WindowMagnificationController(mContext,
+                mHandler, new SfVsyncFrameCallbackProvider(), null,
+                new SurfaceControl.Transaction(), this);
+        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+                mContext, controller);
     }
 
     @Override
@@ -81,9 +86,7 @@
             return;
         }
         mLastConfiguration.setTo(newConfig);
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.onConfigurationChanged(configDiff);
-        }
+        mWindowMagnificationAnimationController.onConfigurationChanged(configDiff);
         if (mModeSwitchesController != null) {
             mModeSwitchesController.onConfigurationChanged(configDiff);
         }
@@ -97,39 +100,25 @@
     @MainThread
     void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController == null) {
-            mWindowMagnificationController = new WindowMagnificationController(mContext,
-                    mHandler,
-                    new SfVsyncFrameCallbackProvider(),
-                    null, new SurfaceControl.Transaction(),
-                    this);
-        }
-        mWindowMagnificationController.enableWindowMagnification(scale, centerX, centerY);
+        mWindowMagnificationAnimationController.enableWindowMagnification(scale, centerX, centerY);
     }
 
     @MainThread
     void setScale(int displayId, float scale) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.setScale(scale);
-        }
+        mWindowMagnificationAnimationController.setScale(scale);
     }
 
     @MainThread
     void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.moveWindowMagnifier(offsetX, offsetY);
-        }
+        mWindowMagnificationAnimationController.moveWindowMagnifier(offsetX, offsetY);
     }
 
     @MainThread
     void disableWindowMagnification(int displayId) {
         //TODO: b/144080869 support multi-display.
-        if (mWindowMagnificationController != null) {
-            mWindowMagnificationController.deleteWindowMagnification();
-        }
-        mWindowMagnificationController = null;
+        mWindowMagnificationAnimationController.deleteWindowMagnification();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
new file mode 100644
index 0000000..ae51623
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationAnimationController.java
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+import android.view.animation.AccelerateInterpolator;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Provides same functionality of {@link WindowMagnificationController}. Some methods run with
+ * the animation.
+ */
+class WindowMagnificationAnimationController implements ValueAnimator.AnimatorUpdateListener,
+        Animator.AnimatorListener {
+
+    private static final String TAG = "WindowMagnificationBridge";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATE_DISABLED, STATE_ENABLED, STATE_DISABLING, STATE_ENABLING})
+    @interface MagnificationState {}
+
+    //The window magnification is disabled.
+    private static final int STATE_DISABLED = 0;
+    //The window magnification is enabled.
+    private static final int STATE_ENABLED = 1;
+    //The window magnification is going to be disabled when the animation is end.
+    private  static final int STATE_DISABLING = 2;
+    //The animation is running for enabling the window magnification.
+    private static final int STATE_ENABLING = 3;
+
+    private final WindowMagnificationController mController;
+    private final ValueAnimator mValueAnimator;
+    private final AnimationSpec mStartSpec = new AnimationSpec();
+    private final AnimationSpec mEndSpec = new AnimationSpec();
+    private final Context mContext;
+
+    @MagnificationState
+    private int mState = STATE_DISABLED;
+
+    WindowMagnificationAnimationController(
+            Context context, WindowMagnificationController controller) {
+        this(context, controller, newValueAnimator(context.getResources()));
+    }
+
+    @VisibleForTesting
+    WindowMagnificationAnimationController(Context context,
+            WindowMagnificationController controller, ValueAnimator valueAnimator) {
+        mContext = context;
+        mController = controller;
+        mValueAnimator = valueAnimator;
+        mValueAnimator.addUpdateListener(this);
+        mValueAnimator.addListener(this);
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#enableWindowMagnification(float, float, float)}
+     * with transition animation. If the window magnification is not enabled, the scale will start
+     * from 1.0 and the center won't be changed during the animation. If {@link #mState} is
+     * {@code STATE_DISABLING}, the animation runs in reverse.
+     *
+     * @param scale   the target scale, or {@link Float#NaN} to leave unchanged.
+     * @param centerX the screen-relative X coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     * @param centerY the screen-relative Y coordinate around which to center,
+     *                or {@link Float#NaN} to leave unchanged.
+     *
+     * @see #onAnimationUpdate(ValueAnimator)
+     */
+    void enableWindowMagnification(float scale, float centerX, float centerY) {
+        if (mState == STATE_ENABLING) {
+            mValueAnimator.cancel();
+        }
+        setupEnableAnimationSpecs(scale, centerX, centerY);
+
+        if (mEndSpec.equals(mStartSpec)) {
+            setState(STATE_ENABLED);
+        } else {
+            if (mState == STATE_DISABLING) {
+                mValueAnimator.reverse();
+            } else {
+                mValueAnimator.start();
+            }
+            setState(STATE_ENABLING);
+        }
+    }
+
+    private void setupEnableAnimationSpecs(float scale, float centerX, float centerY) {
+        final float currentScale = mController.getScale();
+        final float currentCenterX = mController.getCenterX();
+        final float currentCenterY = mController.getCenterY();
+
+        if (mState == STATE_DISABLED) {
+            //We don't need to offset the center during the animation.
+            mStartSpec.set(/* scale*/ 1.0f, centerX, centerY);
+            mEndSpec.set(Float.isNaN(scale) ? mContext.getResources().getInteger(
+                    R.integer.magnification_default_scale) : scale, centerX, centerY);
+        } else {
+            mStartSpec.set(currentScale, currentCenterX, currentCenterY);
+            mEndSpec.set(Float.isNaN(scale) ? currentScale : scale,
+                    Float.isNaN(centerX) ? currentCenterX : centerX,
+                    Float.isNaN(centerY) ? currentCenterY : centerY);
+        }
+        if (DEBUG) {
+            Log.d(TAG, "SetupEnableAnimationSpecs : mStartSpec = " + mStartSpec + ", endSpec = "
+                    + mEndSpec);
+        }
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#setScale(float)}. If the animation is
+     * running, it has no effect.
+     */
+    void setScale(float scale) {
+        if (mValueAnimator.isRunning()) {
+            return;
+        }
+        mController.setScale(scale);
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#deleteWindowMagnification()}} with transition
+     * animation. If the window magnification is enabling, it runs the animation in reverse.
+     */
+    void deleteWindowMagnification() {
+        if (mState == STATE_DISABLED || mState == STATE_DISABLING) {
+            return;
+        }
+        mStartSpec.set(/* scale*/ 1.0f, Float.NaN, Float.NaN);
+        mEndSpec.set(/* scale*/ mController.getScale(), Float.NaN, Float.NaN);
+
+        mValueAnimator.reverse();
+        setState(STATE_DISABLING);
+    }
+
+    /**
+     * Wraps {@link WindowMagnificationController#moveWindowMagnifier(float, float)}. If the
+     * animation is running, it has no effect.
+     * @param offsetX the amount in pixels to offset the window magnifier in the X direction, in
+     *                current screen pixels.
+     * @param offsetY the amount in pixels to offset the window magnifier in the Y direction, in
+     *                current screen pixels.
+     */
+    void moveWindowMagnifier(float offsetX, float offsetY) {
+        if (mValueAnimator.isRunning()) {
+            return;
+        }
+        mController.moveWindowMagnifier(offsetX, offsetY);
+    }
+
+    void onConfigurationChanged(int configDiff) {
+        mController.onConfigurationChanged(configDiff);
+    }
+
+    private void setState(@MagnificationState int state) {
+        if (DEBUG) {
+            Log.d(TAG, "setState from " + mState + " to " + state);
+        }
+        mState = state;
+    }
+
+    @Override
+    public void onAnimationStart(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animation) {
+        if (mState == STATE_DISABLING) {
+            mController.deleteWindowMagnification();
+            setState(STATE_DISABLED);
+        } else if (mState == STATE_ENABLING) {
+            setState(STATE_ENABLED);
+        } else {
+            Log.w(TAG, "onAnimationEnd unexpected state:" + mState);
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animation) {
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        final float fract = animation.getAnimatedFraction();
+        final float sentScale = mStartSpec.mScale + (mEndSpec.mScale - mStartSpec.mScale) * fract;
+        final float centerX =
+                mStartSpec.mCenterX + (mEndSpec.mCenterX - mStartSpec.mCenterX) * fract;
+        final float centerY =
+                mStartSpec.mCenterY + (mEndSpec.mCenterY - mStartSpec.mCenterY) * fract;
+        mController.enableWindowMagnification(sentScale, centerX, centerY);
+    }
+
+    private static ValueAnimator newValueAnimator(Resources resources) {
+        final ValueAnimator valueAnimator = new ValueAnimator();
+        valueAnimator.setDuration(
+                resources.getInteger(com.android.internal.R.integer.config_longAnimTime));
+        valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
+        valueAnimator.setFloatValues(0.0f, 1.0f);
+        return valueAnimator;
+    }
+
+    private static class AnimationSpec {
+        private float mScale = Float.NaN;
+        private float mCenterX = Float.NaN;
+        private float mCenterY = Float.NaN;
+
+        @Override
+        public boolean equals(Object other) {
+            if (this == other) {
+                return true;
+            }
+
+            if (other == null || getClass() != other.getClass()) {
+                return false;
+            }
+
+            final AnimationSpec s = (AnimationSpec) other;
+            return mScale == s.mScale && mCenterX == s.mCenterX && mCenterY == s.mCenterY;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = (mScale != +0.0f ? Float.floatToIntBits(mScale) : 0);
+            result = 31 * result + (mCenterX != +0.0f ? Float.floatToIntBits(mCenterX) : 0);
+            result = 31 * result + (mCenterY != +0.0f ? Float.floatToIntBits(mCenterY) : 0);
+            return result;
+        }
+
+        void set(float scale, float centerX, float centerY) {
+            mScale = scale;
+            mCenterX = centerX;
+            mCenterY = centerY;
+        }
+
+        @Override
+        public String toString() {
+            return "AnimationSpec{"
+                    + "mScale=" + mScale
+                    + ", mCenterX=" + mCenterX
+                    + ", mCenterY=" + mCenterY
+                    + '}';
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 798b751..6d3e8ba 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -150,7 +150,7 @@
 
         mMirrorViewGeometryVsyncCallback =
                 l -> {
-                    if (mMirrorView != null && mMirrorSurface != null) {
+                    if (isWindowVisible() && mMirrorSurface != null) {
                         calculateSourceBounds(mMagnificationFrame, mScale);
                         // The final destination for the magnification surface should be at 0,0
                         // since the ViewRootImpl's position will change
@@ -502,7 +502,7 @@
     /**
      * Enables window magnification with specified parameters.
      *
-     * @param scale   the target scale
+     * @param scale   the target scale, or {@link Float#NaN} to leave unchanged
      * @param centerX the screen-relative X coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
      * @param centerY the screen-relative Y coordinate around which to center,
@@ -513,10 +513,10 @@
                 : centerX - mMagnificationFrame.exactCenterX();
         final float offsetY = Float.isNaN(centerY) ? 0
                 : centerY - mMagnificationFrame.exactCenterY();
-        mScale = scale;
+        mScale = Float.isNaN(scale) ? mScale : scale;
         setMagnificationFrameBoundary();
         updateMagnificationFramePosition((int) offsetX, (int) offsetY);
-        if (mMirrorView == null) {
+        if (!isWindowVisible()) {
             createMirrorWindow();
             showControls();
         } else {
@@ -527,10 +527,10 @@
     /**
      * Sets the scale of the magnified region if it's visible.
      *
-     * @param scale the target scale
+     * @param scale the target scale, or {@link Float#NaN} to leave unchanged
      */
     void setScale(float scale) {
-        if (mMirrorView == null || mScale == scale) {
+        if (!isWindowVisible() || mScale == scale) {
             return;
         }
         enableWindowMagnification(scale, Float.NaN, Float.NaN);
@@ -552,4 +552,35 @@
             modifyWindowMagnification(mTransaction);
         }
     }
+
+    /**
+     * Gets the scale.
+     * @return {@link Float#NaN} if the window is invisible.
+     */
+    float getScale() {
+        return isWindowVisible() ? mScale : Float.NaN;
+    }
+
+    /**
+     * Returns the screen-relative X coordinate of the center of the magnified bounds.
+     *
+     * @return the X coordinate. {@link Float#NaN} if the window is invisible.
+     */
+    float getCenterX() {
+        return isWindowVisible() ? mMagnificationFrame.exactCenterX() : Float.NaN;
+    }
+
+    /**
+     * Returns the screen-relative Y coordinate of the center of the magnified bounds.
+     *
+     * @return the Y coordinate. {@link Float#NaN} if the window is invisible.
+     */
+    float getCenterY() {
+        return isWindowVisible() ? mMagnificationFrame.exactCenterY() : Float.NaN;
+    }
+
+    //The window is visible when it is existed.
+    private boolean isWindowVisible() {
+        return mMirrorView != null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 4df6660..6512624 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -281,9 +281,11 @@
      * @return {@code true} iff the app-op for should be shown to the user
      */
     private boolean isUserVisible(int appOpCode, int uid, String packageName) {
-        // currently OP_SYSTEM_ALERT_WINDOW does not correspond to a platform permission
-        // which may be user senstive, so for now always show it to the user.
-        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW) {
+        // currently OP_SYSTEM_ALERT_WINDOW and OP_MONITOR_HIGH_POWER_LOCATION
+        // does not correspond to a platform permission
+        // which may be user sensitive, so for now always show it to the user.
+        if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
+                || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index d5a14f7..affc5ee 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -45,7 +45,6 @@
 
 @Singleton
 class PrivacyItemController @Inject constructor(
-    context: Context,
     private val appOpsController: AppOpsController,
     @Main uiExecutor: DelayableExecutor,
     @Background private val bgExecutor: Executor,
@@ -57,16 +56,21 @@
 
     @VisibleForTesting
     internal companion object {
-        val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO,
+        val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
+                AppOpsManager.OP_RECORD_AUDIO)
+        val OPS_LOCATION = intArrayOf(
                 AppOpsManager.OP_COARSE_LOCATION,
                 AppOpsManager.OP_FINE_LOCATION)
+        val OPS = OPS_MIC_CAMERA + OPS_LOCATION
         val intentFilter = IntentFilter().apply {
             addAction(Intent.ACTION_USER_SWITCHED)
             addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
             addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE)
         }
         const val TAG = "PrivacyItemController"
+        private const val ALL_INDICATORS =
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
     }
 
     @VisibleForTesting
@@ -74,9 +78,14 @@
         @Synchronized get() = field.toList() // Returns a shallow copy of the list
         @Synchronized set
 
-    private fun isPermissionsHubEnabled(): Boolean {
+    private fun isAllIndicatorsEnabled(): Boolean {
         return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
+                ALL_INDICATORS, false)
+    }
+
+    private fun isMicCameraEnabled(): Boolean {
+        return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                MIC_CAMERA, false)
     }
 
     private var currentUserIds = emptyList<Int>()
@@ -94,23 +103,28 @@
         uiExecutor.execute(notifyChanges)
     }
 
-    var indicatorsAvailable = isPermissionsHubEnabled()
+    var allIndicatorsAvailable = isAllIndicatorsEnabled()
         private set
-    @VisibleForTesting
-    internal val devicePropertiesChangedListener =
+    var micCameraAvailable = isMicCameraEnabled()
+        private set
+
+    private val devicePropertiesChangedListener =
             object : DeviceConfig.OnPropertiesChangedListener {
         override fun onPropertiesChanged(properties: DeviceConfig.Properties) {
             if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) &&
-                    properties.getKeyset().contains(
-                    SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED)) {
-                val flag = properties.getBoolean(
-                        SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false)
-                if (indicatorsAvailable != flag) {
-                    // This is happening already in the UI executor, so we can iterate in the
-                    indicatorsAvailable = flag
-                    callbacks.forEach { it.get()?.onFlagChanged(flag) }
+                    (properties.keyset.contains(ALL_INDICATORS) ||
+                    properties.keyset.contains(MIC_CAMERA))) {
+
+                // Running on the ui executor so can iterate on callbacks
+                if (properties.keyset.contains(ALL_INDICATORS)) {
+                    allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, false)
+                    callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) }
                 }
 
+                if (properties.keyset.contains(MIC_CAMERA)) {
+                    micCameraAvailable = properties.getBoolean(MIC_CAMERA, false)
+                    callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) }
+                }
                 internalUiExecutor.updateListeningState()
             }
         }
@@ -123,6 +137,10 @@
             packageName: String,
             active: Boolean
         ) {
+            // Check if we care about this code right now
+            if (!allIndicatorsAvailable && code in OPS_LOCATION) {
+                return
+            }
             val userId = UserHandle.getUserId(uid)
             if (userId in currentUserIds) {
                 update(false)
@@ -166,13 +184,16 @@
     }
 
     /**
-     * Updates listening status based on whether there are callbacks and the indicators are enabled
+     * Updates listening status based on whether there are callbacks and the indicators are enabled.
+     *
+     * Always listen to all OPS so we don't have to figure out what we should be listening to. We
+     * still have to filter anyway. Updates are filtered in the callback.
      *
      * This is only called from private (add/remove)Callback and from the config listener, all in
      * main thread.
      */
     private fun setListeningState() {
-        val listen = !callbacks.isEmpty() and indicatorsAvailable
+        val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable)
         if (listening == listen) return
         listening = listen
         if (listening) {
@@ -233,14 +254,19 @@
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
+        if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null
         val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid)
         return PrivacyItem(type, app)
     }
 
     interface Callback {
         fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
+
         @JvmDefault
-        fun onFlagChanged(flag: Boolean) {}
+        fun onFlagAllChanged(flag: Boolean) {}
+
+        @JvmDefault
+        fun onFlagMicCameraChanged(flag: Boolean) {}
     }
 
     internal inner class Receiver : BroadcastReceiver() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 2dc82dd..2e258d5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -151,7 +151,8 @@
     private Space mSpace;
     private BatteryMeterView mBatteryRemainingIcon;
     private RingerModeTracker mRingerModeTracker;
-    private boolean mPermissionsHubEnabled;
+    private boolean mAllIndicatorsEnabled;
+    private boolean mMicCameraIndicatorsEnabled;
 
     private PrivacyItemController mPrivacyItemController;
     private final UiEventLogger mUiEventLogger;
@@ -178,13 +179,26 @@
         }
 
         @Override
-        public void onFlagChanged(boolean flag) {
-            if (mPermissionsHubEnabled != flag) {
-                StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
-                iconContainer.setIgnoredSlots(getIgnoredIconSlots());
-                setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+        public void onFlagAllChanged(boolean flag) {
+            if (mAllIndicatorsEnabled != flag) {
+                mAllIndicatorsEnabled = flag;
+                update();
             }
         }
+
+        @Override
+        public void onFlagMicCameraChanged(boolean flag) {
+            if (mMicCameraIndicatorsEnabled != flag) {
+                mMicCameraIndicatorsEnabled = flag;
+                update();
+            }
+        }
+
+        private void update() {
+            StatusIconContainer iconContainer = requireViewById(R.id.statusIcons);
+            iconContainer.setIgnoredSlots(getIgnoredIconSlots());
+            setChipVisibility(!mPrivacyChip.getPrivacyList().isEmpty());
+        }
     };
 
     @Inject
@@ -267,7 +281,8 @@
         mRingerModeTextView.setSelected(true);
         mNextAlarmTextView.setSelected(true);
 
-        mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+        mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+        mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
     }
 
     public QuickQSPanel getHeaderQsPanel() {
@@ -276,13 +291,15 @@
 
     private List<String> getIgnoredIconSlots() {
         ArrayList<String> ignored = new ArrayList<>();
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_camera));
-        ignored.add(mContext.getResources().getString(
-                com.android.internal.R.string.status_bar_microphone));
-        if (mPermissionsHubEnabled) {
+        if (getChipEnabled()) {
             ignored.add(mContext.getResources().getString(
-                    com.android.internal.R.string.status_bar_location));
+                    com.android.internal.R.string.status_bar_camera));
+            ignored.add(mContext.getResources().getString(
+                    com.android.internal.R.string.status_bar_microphone));
+            if (mAllIndicatorsEnabled) {
+                ignored.add(mContext.getResources().getString(
+                        com.android.internal.R.string.status_bar_location));
+            }
         }
 
         return ignored;
@@ -300,7 +317,7 @@
     }
 
     private void setChipVisibility(boolean chipVisible) {
-        if (chipVisible && mPermissionsHubEnabled) {
+        if (chipVisible && getChipEnabled()) {
             mPrivacyChip.setVisibility(View.VISIBLE);
             // Makes sure that the chip is logged as viewed at most once each time QS is opened
             // mListening makes sure that the callback didn't return after the user closed QS
@@ -607,7 +624,8 @@
             mAlarmController.addCallback(this);
             mLifecycle.setCurrentState(Lifecycle.State.RESUMED);
             // Get the most up to date info
-            mPermissionsHubEnabled = mPrivacyItemController.getIndicatorsAvailable();
+            mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
+            mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
             mPrivacyItemController.addCallback(mPICCallback);
         } else {
             mZenController.removeCallback(this);
@@ -747,4 +765,8 @@
             updateHeaderTextContainerAlphaAnimator();
         }
     }
+
+    private boolean getChipEnabled() {
+        return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 8c485a6..d2aaaede 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -451,15 +451,17 @@
         if (listening) {
             if (mListeners.add(listener) && mListeners.size() == 1) {
                 if (DEBUG) Log.d(TAG, "handleSetListening true");
-                mLifecycle.setCurrentState(RESUMED);
                 handleSetListening(listening);
-                refreshState(); // Ensure we get at least one refresh after listening.
+                mUiHandler.post(() -> {
+                    mLifecycle.setCurrentState(RESUMED);
+                    refreshState(); // Ensure we get at least one refresh after listening.
+                });
             }
         } else {
             if (mListeners.remove(listener) && mListeners.size() == 0) {
                 if (DEBUG) Log.d(TAG, "handleSetListening false");
-                mLifecycle.setCurrentState(STARTED);
                 handleSetListening(listening);
+                mUiHandler.post(() -> mLifecycle.setCurrentState(STARTED));
             }
         }
         updateIsFullQs();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 4007abb..d40b666 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -78,6 +78,22 @@
                             onUndockingTask();
                         }
                     }
+
+                    @Override
+                    public void onActivityForcedResizable(String packageName, int taskId,
+                            int reason) {
+                        mDividerController.onActivityForcedResizable(packageName, taskId, reason);
+                    }
+
+                    @Override
+                    public void onActivityDismissingDockedStack() {
+                        mDividerController.onActivityDismissingSplitScreen();
+                    }
+
+                    @Override
+                    public void onActivityLaunchOnSecondaryDisplayFailed() {
+                        mDividerController.onActivityLaunchOnSecondaryDisplayFailed();
+                    }
                 }
         );
     }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
index 81649f6..1ee8abb 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
@@ -50,8 +50,7 @@
 /**
  * Controls the docked stack divider.
  */
-public class DividerController implements DividerView.DividerCallbacks,
-        DisplayController.OnDisplaysChangedListener {
+public class DividerController implements DisplayController.OnDisplaysChangedListener {
     static final boolean DEBUG = false;
     private static final String TAG = "Divider";
 
@@ -257,8 +256,8 @@
         mView = (DividerView)
                 LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
         DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
-        mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
-                mImePositionProcessor, mWindowManagerProxy);
+        mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits,
+                mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
         mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
         mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
         final int size = dctx.getResources().getDimensionPixelSize(
@@ -397,7 +396,22 @@
         }
     }
 
-    /** Called when there's a task undocking.  */
+    /** Called when there's an activity forced resizable. */
+    public void onActivityForcedResizable(String packageName, int taskId, int reason) {
+        mForcedResizableController.activityForcedResizable(packageName, taskId, reason);
+    }
+
+    /** Called when there's an activity dismissing split screen. */
+    public void onActivityDismissingSplitScreen() {
+        mForcedResizableController.activityDismissingSplitScreen();
+    }
+
+    /** Called when there's an activity launch on secondary display failed. */
+    public void onActivityLaunchOnSecondaryDisplayFailed() {
+        mForcedResizableController.activityLaunchOnSecondaryDisplayFailed();
+    }
+
+    /** Called when there's a task undocking. */
     public void onUndockingTask() {
         if (mView != null) {
             mView.onUndockingTask();
@@ -426,17 +440,7 @@
         mForcedResizableController.onAppTransitionFinished();
     }
 
-    @Override
-    public void onDraggingStart() {
-        mForcedResizableController.onDraggingStart();
-    }
-
-    @Override
-    public void onDraggingEnd() {
-        mForcedResizableController.onDraggingEnd();
-    }
-
-    /** Dumps current status of Divider.*/
+    /** Dumps current status of Split Screen. */
     public void dump(PrintWriter pw) {
         pw.print("  mVisible="); pw.println(mVisible);
         pw.print("  mMinimized="); pw.println(mMinimized);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index f412cc0..ff8bab0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -28,15 +28,13 @@
 import android.widget.Toast;
 
 import com.android.systemui.R;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
 
 import java.util.function.Consumer;
 
 /**
  * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
  */
-public class ForcedResizableInfoActivityController {
+final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks {
 
     private static final String SELF_PACKAGE_NAME = "com.android.systemui";
 
@@ -47,12 +45,7 @@
     private final ArraySet<String> mPackagesShownInSession = new ArraySet<>();
     private boolean mDividerDragging;
 
-    private final Runnable mTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            showPending();
-        }
-    };
+    private final Runnable mTimeoutRunnable = this::showPending;
 
     private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
         if (!exists) {
@@ -78,44 +71,28 @@
     public ForcedResizableInfoActivityController(Context context,
             DividerController dividerController) {
         mContext = context;
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(
-                new TaskStackChangeListener() {
-                    @Override
-                    public void onActivityForcedResizable(String packageName, int taskId,
-                            int reason) {
-                        activityForcedResizable(packageName, taskId, reason);
-                    }
-
-                    @Override
-                    public void onActivityDismissingDockedStack() {
-                        activityDismissingDockedStack();
-                    }
-
-                    @Override
-                    public void onActivityLaunchOnSecondaryDisplayFailed() {
-                        activityLaunchOnSecondaryDisplayFailed();
-                    }
-                });
         dividerController.registerInSplitScreenListener(mDockedStackExistsListener);
     }
 
-    public void onAppTransitionFinished() {
+    @Override
+    public void onDraggingStart() {
+        mDividerDragging = true;
+        mHandler.removeCallbacks(mTimeoutRunnable);
+    }
+
+    @Override
+    public void onDraggingEnd() {
+        mDividerDragging = false;
+        showPending();
+    }
+
+    void onAppTransitionFinished() {
         if (!mDividerDragging) {
             showPending();
         }
     }
 
-    void onDraggingStart() {
-        mDividerDragging = true;
-        mHandler.removeCallbacks(mTimeoutRunnable);
-    }
-
-    void onDraggingEnd() {
-        mDividerDragging = false;
-        showPending();
-    }
-
-    private void activityForcedResizable(String packageName, int taskId, int reason) {
+    void activityForcedResizable(String packageName, int taskId, int reason) {
         if (debounce(packageName)) {
             return;
         }
@@ -123,12 +100,12 @@
         postTimeout();
     }
 
-    private void activityDismissingDockedStack() {
+    void activityDismissingSplitScreen() {
         Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
                 Toast.LENGTH_SHORT).show();
     }
 
-    private void activityLaunchOnSecondaryDisplayFailed() {
+    void activityLaunchOnSecondaryDisplayFailed() {
         Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text,
                 Toast.LENGTH_SHORT).show();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index b2cfcea..8cb54ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -662,16 +662,18 @@
 
         mIconController.setIconVisibility(mSlotCamera, showCamera);
         mIconController.setIconVisibility(mSlotMicrophone, showMicrophone);
-        mIconController.setIconVisibility(mSlotLocation, showLocation);
+        if (mPrivacyItemController.getAllIndicatorsAvailable()) {
+            mIconController.setIconVisibility(mSlotLocation, showLocation);
+        }
     }
 
     @Override
     public void onLocationActiveChanged(boolean active) {
-        if (!mPrivacyItemController.getIndicatorsAvailable()) updateLocation();
+        if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController();
     }
 
     // Updates the status view based on the current state of location requests.
-    private void updateLocation() {
+    private void updateLocationFromController() {
         if (mLocationController.isLocationActive()) {
             mIconController.setIconVisibility(mSlotLocation, true);
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index b89cb21..8ff7a41 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -86,7 +86,7 @@
     static ArraySet<String> getIconHideList(Context context, String hideListStr) {
         ArraySet<String> ret = new ArraySet<>();
         String[] hideList = hideListStr == null
-            ? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
+            ? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
             : hideListStr.split(",");
         for (String slot : hideList) {
             if (!TextUtils.isEmpty(slot)) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
index fbc8e9d..ac567e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IWindowMagnificationConnectionTest.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IWindowMagnificationConnection;
@@ -47,6 +48,7 @@
  */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class IWindowMagnificationConnectionTest extends SysuiTestCase {
 
     private static final int TEST_DISPLAY = Display.DEFAULT_DISPLAY;
@@ -57,7 +59,7 @@
     @Mock
     private IWindowMagnificationConnectionCallback mConnectionCallback;
     @Mock
-    private WindowMagnificationController mWindowMagnificationController;
+    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
     @Mock
     private ModeSwitchesController mModeSwitchesController;
     private IWindowMagnificationConnection mIWindowMagnificationConnection;
@@ -74,7 +76,8 @@
                 any(IWindowMagnificationConnection.class));
         mWindowMagnification = new WindowMagnification(getContext(),
                 getContext().getMainThreadHandler(), mCommandQueue, mModeSwitchesController);
-        mWindowMagnification.mWindowMagnificationController = mWindowMagnificationController;
+        mWindowMagnification.mWindowMagnificationAnimationController =
+                mWindowMagnificationAnimationController;
         mWindowMagnification.requestWindowMagnificationConnection(true);
         assertNotNull(mIWindowMagnificationConnection);
         mIWindowMagnificationConnection.setConnectionCallback(mConnectionCallback);
@@ -86,7 +89,7 @@
                 Float.NaN);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).enableWindowMagnification(3.0f, Float.NaN,
+        verify(mWindowMagnificationAnimationController).enableWindowMagnification(3.0f, Float.NaN,
                 Float.NaN);
     }
 
@@ -99,7 +102,7 @@
         mIWindowMagnificationConnection.disableWindowMagnification(TEST_DISPLAY);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).deleteWindowMagnification();
+        verify(mWindowMagnificationAnimationController).deleteWindowMagnification();
     }
 
     @Test
@@ -107,7 +110,7 @@
         mIWindowMagnificationConnection.setScale(TEST_DISPLAY, 3.0f);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).setScale(3.0f);
+        verify(mWindowMagnificationAnimationController).setScale(3.0f);
     }
 
     @Test
@@ -115,7 +118,7 @@
         mIWindowMagnificationConnection.moveWindowMagnifier(TEST_DISPLAY, 100f, 200f);
         waitForIdleSync();
 
-        verify(mWindowMagnificationController).moveWindowMagnifier(100f, 200f);
+        verify(mWindowMagnificationAnimationController).moveWindowMagnifier(100f, 200f);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
new file mode 100644
index 0000000..add0843
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -0,0 +1,362 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.accessibility;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.animation.ValueAnimator;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.SurfaceControl;
+import android.view.animation.AccelerateInterpolator;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+
+@MediumTest
+@RunWith(AndroidTestingRunner.class)
+public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
+
+    private static final float DEFAULT_SCALE = 3.0f;
+    private static final float DEFAULT_CENTER_X = 400.0f;
+    private static final float DEFAULT_CENTER_Y = 500.0f;
+    private static final long ANIMATION_DURATION_MS = 100;
+
+    private AtomicReference<Float> mCurrentScale = new AtomicReference<>((float) 0);
+    private AtomicReference<Float> mCurrentCenterX = new AtomicReference<>((float) 0);
+    private AtomicReference<Float> mCurrentCenterY = new AtomicReference<>((float) 0);
+    private ArgumentCaptor<Float> mScaleCaptor = ArgumentCaptor.forClass(Float.class);
+    private ArgumentCaptor<Float> mCenterXCaptor = ArgumentCaptor.forClass(Float.class);
+    private ArgumentCaptor<Float> mCenterYCaptor = ArgumentCaptor.forClass(Float.class);
+
+    @Mock
+    Handler mHandler;
+    @Mock
+    SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+    @Mock
+    WindowMagnifierCallback mWindowMagnifierCallback;
+
+    private SpyWindowMagnificationController mController;
+    private WindowMagnificationController mSpyController;
+    private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
+    private Instrumentation mInstrumentation;
+    private long mWaitingAnimationPeriod;
+    private long mWaitIntermediateAnimationPeriod;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mWaitingAnimationPeriod = ANIMATION_DURATION_MS + 50;
+        mWaitIntermediateAnimationPeriod = ANIMATION_DURATION_MS / 2;
+        mController = new SpyWindowMagnificationController(mContext, mHandler,
+                mSfVsyncFrameProvider, null, new SurfaceControl.Transaction(),
+                mWindowMagnifierCallback);
+        mSpyController = mController.getSpyController();
+        mWindowMagnificationAnimationController = new WindowMagnificationAnimationController(
+                mContext, mController, newValueAnimator());
+    }
+
+    @Test
+    public void enableWindowMagnification_disabled_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(
+                mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verifyStartValue(mScaleCaptor, 1.0f);
+        verifyStartValue(mCenterXCaptor, DEFAULT_CENTER_X);
+        verifyStartValue(mCenterYCaptor, DEFAULT_CENTER_Y);
+        verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+    }
+
+    @Test
+    public void enableWindowMagnification_enabling_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+        final float targetScale = DEFAULT_SCALE + 1.0f;
+        final float targetCenterX = DEFAULT_CENTER_X + 100;
+        final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+        mInstrumentation.runOnMainSync(() -> {
+            Mockito.reset(mSpyController);
+            mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                    targetCenterX, targetCenterY);
+            mCurrentScale.set(mController.getScale());
+            mCurrentCenterX.set(mController.getCenterX());
+            mCurrentCenterY.set(mController.getCenterY());
+        });
+
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verifyStartValue(mScaleCaptor, mCurrentScale.get());
+        verifyStartValue(mCenterXCaptor, mCurrentCenterX.get());
+        verifyStartValue(mCenterYCaptor, mCurrentCenterY.get());
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+    }
+
+    @Test
+    public void enableWindowMagnification_disabling_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+        final float targetScale = DEFAULT_SCALE + 1.0f;
+        final float targetCenterX = DEFAULT_CENTER_X + 100;
+        final float targetCenterY = DEFAULT_CENTER_Y + 100;
+
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
+                            targetCenterX, targetCenterY);
+                    mCurrentScale.set(mController.getScale());
+                    mCurrentCenterX.set(mController.getCenterX());
+                    mCurrentCenterY.set(mController.getCenterY());
+                });
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(
+                mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        //Animating in reverse, so we only check if the start values are greater than current.
+        assertTrue(mScaleCaptor.getAllValues().get(0) > mCurrentScale.get());
+        assertEquals(targetScale, mScaleCaptor.getValue(), 0f);
+        assertTrue(mCenterXCaptor.getAllValues().get(0) > mCurrentCenterX.get());
+        assertEquals(targetCenterX, mCenterXCaptor.getValue(), 0f);
+        assertTrue(mCenterYCaptor.getAllValues().get(0) > mCurrentCenterY.get());
+        assertEquals(targetCenterY, mCenterYCaptor.getValue(), 0f);
+        verifyFinalSpec(targetScale, targetCenterX, targetCenterY);
+    }
+
+    @Test
+    public void enableWindowMagnificationWithSameScale_doNothing() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, never()).enableWindowMagnification(anyFloat(), anyFloat(),
+                anyFloat());
+    }
+
+    @Test
+    public void setScale_enabled_expectedScale() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationAnimationController.setScale(DEFAULT_SCALE + 1));
+
+        verify(mSpyController).setScale(DEFAULT_SCALE + 1);
+        verifyFinalSpec(DEFAULT_SCALE + 1, DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+    }
+
+    @Test
+    public void deleteWindowMagnification_enabled_expectedStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verify(mSpyController).deleteWindowMagnification();
+        verifyStartValue(mScaleCaptor, DEFAULT_SCALE);
+        verifyStartValue(mCenterXCaptor, Float.NaN);
+        verifyStartValue(mCenterYCaptor, Float.NaN);
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
+    public void deleteWindowMagnification_disabled_doNothing() {
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        Mockito.verifyNoMoreInteractions(mSpyController);
+    }
+
+    @Test
+    public void deleteWindowMagnification_enabling_checkStartAndEndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+
+        //It just reverse the animation, so we don't need to wait the whole duration.
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.deleteWindowMagnification();
+                    mCurrentScale.set(mController.getScale());
+                    mCurrentCenterX.set(mController.getCenterX());
+                    mCurrentCenterY.set(mController.getCenterY());
+                });
+        SystemClock.sleep(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verify(mSpyController).deleteWindowMagnification();
+
+        //The animation is in verse, so we only check the start values should no be greater than
+        // the current one.
+        assertTrue(mScaleCaptor.getAllValues().get(0) <= mCurrentScale.get());
+        assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+        verifyStartValue(mCenterXCaptor, Float.NaN);
+        verifyStartValue(mCenterYCaptor, Float.NaN);
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
+    public void deleteWindowMagnification_disabling_checkStartAndValues() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+        deleteWindowMagnificationAndWaitAnimating(mWaitIntermediateAnimationPeriod);
+
+        deleteWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        verify(mSpyController, atLeast(2)).enableWindowMagnification(mScaleCaptor.capture(),
+                mCenterXCaptor.capture(), mCenterYCaptor.capture());
+        verify(mSpyController).deleteWindowMagnification();
+        assertEquals(1.0f, mScaleCaptor.getValue(), 0f);
+        verifyFinalSpec(Float.NaN, Float.NaN, Float.NaN);
+    }
+
+    @Test
+    public void moveWindowMagnifier_enabled() {
+        enableWindowMagnificationAndWaitAnimating(mWaitingAnimationPeriod);
+
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationAnimationController.moveWindowMagnifier(100f, 200f));
+
+        verify(mSpyController).moveWindowMagnifier(100f, 200f);
+        verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X + 100f, DEFAULT_CENTER_Y + 100f);
+    }
+
+    @Test
+    public void onConfigurationChanged_passThrough() {
+        mWindowMagnificationAnimationController.onConfigurationChanged(100);
+
+        verify(mSpyController).onConfigurationChanged(100);
+    }
+    private void verifyFinalSpec(float expectedScale, float expectedCenterX,
+            float expectedCenterY) {
+        assertEquals(expectedScale, mController.getScale(), 0f);
+        assertEquals(expectedCenterX, mController.getCenterX(), 0f);
+        assertEquals(expectedCenterY, mController.getCenterY(), 0f);
+    }
+
+    private void enableWindowMagnificationAndWaitAnimating(long duration) {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    Mockito.reset(mSpyController);
+                    mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
+                            DEFAULT_CENTER_X, DEFAULT_CENTER_Y);
+                });
+        SystemClock.sleep(duration);
+    }
+
+    private void deleteWindowMagnificationAndWaitAnimating(long duration) {
+        mInstrumentation.runOnMainSync(
+                () -> {
+                    resetMockObjects();
+                    mWindowMagnificationAnimationController.deleteWindowMagnification();
+                });
+        SystemClock.sleep(duration);
+    }
+
+    private void verifyStartValue(ArgumentCaptor<Float> captor, float startValue) {
+        assertEquals(startValue, captor.getAllValues().get(0), 0f);
+    }
+
+    private void resetMockObjects() {
+        Mockito.reset(mSpyController);
+    }
+
+    /**
+     * It observes the methods in {@link WindowMagnificationController} since we couldn't spy it
+     * directly.
+     */
+    private static class SpyWindowMagnificationController extends WindowMagnificationController {
+        private WindowMagnificationController mSpyController;
+
+        SpyWindowMagnificationController(Context context, Handler handler,
+                SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
+                MirrorWindowControl mirrorWindowControl, SurfaceControl.Transaction transaction,
+                WindowMagnifierCallback callback) {
+            super(context, handler, sfVsyncFrameProvider, mirrorWindowControl, transaction,
+                    callback);
+            mSpyController = Mockito.mock(WindowMagnificationController.class);
+        }
+
+        WindowMagnificationController getSpyController() {
+            return mSpyController;
+        }
+
+        @Override
+        void enableWindowMagnification(float scale, float centerX, float centerY) {
+            super.enableWindowMagnification(scale, centerX, centerY);
+            mSpyController.enableWindowMagnification(scale, centerX, centerY);
+        }
+
+        @Override
+        void deleteWindowMagnification() {
+            super.deleteWindowMagnification();
+            mSpyController.deleteWindowMagnification();
+        }
+
+        @Override
+        void moveWindowMagnifier(float offsetX, float offsetY) {
+            super.moveWindowMagnifier(offsetX, offsetX);
+            mSpyController.moveWindowMagnifier(offsetX, offsetY);
+        }
+
+        @Override
+        void setScale(float scale) {
+            super.setScale(scale);
+            mSpyController.setScale(scale);
+        }
+
+        @Override
+        void onConfigurationChanged(int configDiff) {
+            super.onConfigurationChanged(configDiff);
+            mSpyController.onConfigurationChanged(configDiff);
+        }
+
+    }
+
+    private static ValueAnimator newValueAnimator() {
+        final ValueAnimator valueAnimator = new ValueAnimator();
+        valueAnimator.setDuration(ANIMATION_DURATION_MS);
+        valueAnimator.setInterpolator(new AccelerateInterpolator(2.5f));
+        valueAnimator.setFloatValues(0.0f, 1.0f);
+        return valueAnimator;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 2007fbb..1515cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.view.Choreographer.FrameCallback;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.atLeastOnce;
@@ -83,9 +84,8 @@
 
     @After
     public void tearDown() {
-        mInstrumentation.runOnMainSync(() -> {
-            mWindowMagnificationController.deleteWindowMagnification();
-        });
+        mInstrumentation.runOnMainSync(
+                () -> mWindowMagnificationController.deleteWindowMagnification());
     }
 
     @Test
@@ -121,4 +121,18 @@
 
         verify(mSfVsyncFrameProvider, atLeastOnce()).postFrameCallback(any());
     }
+
+    @Test
+    public void setScale_expectedValue() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.setScale(3.0f);
+        });
+
+        assertEquals(3.0f, mWindowMagnificationController.getScale(), 0);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
index 4136013..936558b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.IWindowMagnificationConnection;
@@ -45,6 +46,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class WindowMagnificationTest extends SysuiTestCase {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
new file mode 100644
index 0000000..4ba29e6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+package com.android.systemui.privacy
+
+import android.os.UserManager
+import android.provider.DeviceConfig
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.appops.AppOpsController
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.DeviceConfigProxyFake
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class PrivacyItemControllerFlagsTest : SysuiTestCase() {
+    companion object {
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+        fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+        fun <T> any(): T = Mockito.any<T>()
+
+        private const val ALL_INDICATORS =
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
+    }
+
+    @Mock
+    private lateinit var appOpsController: AppOpsController
+    @Mock
+    private lateinit var callback: PrivacyItemController.Callback
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock
+    private lateinit var dumpManager: DumpManager
+
+    private lateinit var privacyItemController: PrivacyItemController
+    private lateinit var executor: FakeExecutor
+    private lateinit var deviceConfigProxy: DeviceConfigProxy
+
+    fun PrivacyItemController(): PrivacyItemController {
+        return PrivacyItemController(
+                appOpsController,
+                executor,
+                executor,
+                broadcastDispatcher,
+                deviceConfigProxy,
+                userManager,
+                dumpManager
+        )
+    }
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        executor = FakeExecutor(FakeSystemClock())
+        deviceConfigProxy = DeviceConfigProxyFake()
+
+        privacyItemController = PrivacyItemController()
+        privacyItemController.addCallback(callback)
+
+        executor.runAllReady()
+    }
+
+    @Test
+    fun testNotListeningByDefault() {
+        assertFalse(privacyItemController.allIndicatorsAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
+
+        verify(appOpsController, never()).addCallback(any(), any())
+    }
+
+    @Test
+    fun testMicCameraChanged() {
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagMicCameraChanged(true)
+        verify(callback, never()).onFlagAllChanged(anyBoolean())
+
+        assertTrue(privacyItemController.micCameraAvailable)
+        assertFalse(privacyItemController.allIndicatorsAvailable)
+    }
+
+    @Test
+    fun testAllChanged() {
+        changeAll(true)
+        executor.runAllReady()
+
+        verify(callback).onFlagAllChanged(true)
+        verify(callback, never()).onFlagMicCameraChanged(anyBoolean())
+
+        assertTrue(privacyItemController.allIndicatorsAvailable)
+        assertFalse(privacyItemController.micCameraAvailable)
+    }
+
+    @Test
+    fun testBothChanged() {
+        changeAll(true)
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        verify(callback, atLeastOnce()).onFlagAllChanged(true)
+        verify(callback, atLeastOnce()).onFlagMicCameraChanged(true)
+
+        assertTrue(privacyItemController.allIndicatorsAvailable)
+        assertTrue(privacyItemController.micCameraAvailable)
+    }
+
+    @Test
+    fun testAll_listeningToAll() {
+        changeAll(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
+    fun testMicCamera_listening() {
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
+    fun testAll_listening() {
+        changeAll(true)
+        executor.runAllReady()
+
+        verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any())
+    }
+
+    @Test
+    fun testAllFalse_notListening() {
+        changeAll(true)
+        executor.runAllReady()
+        changeAll(false)
+        executor.runAllReady()
+
+        verify(appOpsController).removeCallback(any(), any())
+    }
+
+    @Test
+    fun testSomeListening_stillListening() {
+        changeAll(true)
+        changeMicCamera(true)
+        executor.runAllReady()
+        changeAll(false)
+        executor.runAllReady()
+
+        verify(appOpsController, never()).removeCallback(any(), any())
+    }
+
+    @Test
+    fun testAllDeleted_stopListening() {
+        changeAll(true)
+        executor.runAllReady()
+        changeAll(null)
+        executor.runAllReady()
+
+        verify(appOpsController).removeCallback(any(), any())
+    }
+
+    @Test
+    fun testMicDeleted_stopListening() {
+        changeMicCamera(true)
+        executor.runAllReady()
+        changeMicCamera(null)
+        executor.runAllReady()
+
+        verify(appOpsController).removeCallback(any(), any())
+    }
+
+    private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+
+    private fun changeProperty(name: String, value: Boolean?) {
+        deviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                name,
+                value?.toString(),
+                false
+        )
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index dddc350..fb42baa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,7 +18,6 @@
 
 import android.app.ActivityManager
 import android.app.AppOpsManager
-import android.content.Context
 import android.content.Intent
 import android.content.pm.UserInfo
 import android.os.UserHandle
@@ -69,10 +68,11 @@
     companion object {
         val CURRENT_USER_ID = ActivityManager.getCurrentUser()
         val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE
-        const val SYSTEM_UID = 1000
         const val TEST_PACKAGE_NAME = "test"
-        const val DEVICE_SERVICES_STRING = "Device services"
-        const val TAG = "PrivacyItemControllerTest"
+
+        private const val ALL_INDICATORS =
+                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED
+        private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED
         fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
         fun <T> eq(value: T): T = Mockito.eq(value) ?: value
         fun <T> any(): T = Mockito.any<T>()
@@ -97,9 +97,8 @@
     private lateinit var executor: FakeExecutor
     private lateinit var deviceConfigProxy: DeviceConfigProxy
 
-    fun PrivacyItemController(context: Context): PrivacyItemController {
+    fun PrivacyItemController(): PrivacyItemController {
         return PrivacyItemController(
-                context,
                 appOpsController,
                 executor,
                 executor,
@@ -116,11 +115,8 @@
         executor = FakeExecutor(FakeSystemClock())
         deviceConfigProxy = DeviceConfigProxyFake()
 
-        appOpsController = mDependency.injectMockDependency(AppOpsController::class.java)
-
-        deviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "true", false)
+        // Listen to everything by default
+        changeAll(true)
 
         doReturn(listOf(object : UserInfo() {
             init {
@@ -128,7 +124,7 @@
             }
         })).`when`(userManager).getProfiles(anyInt())
 
-        privacyItemController = PrivacyItemController(mContext)
+        privacyItemController = PrivacyItemController()
     }
 
     @Test
@@ -276,15 +272,59 @@
 
     @Test
     fun testNotListeningWhenIndicatorsDisabled() {
-        deviceConfigProxy.setProperty(
-                DeviceConfig.NAMESPACE_PRIVACY,
-                SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED,
-                "false",
-                false
-        )
+        changeAll(false)
         privacyItemController.addCallback(callback)
         executor.runAllReady()
         verify(appOpsController, never()).addCallback(eq(PrivacyItemController.OPS),
                 any())
     }
+
+    @Test
+    fun testNotSendingLocationWhenOnlyMicCamera() {
+        changeAll(false)
+        changeMicCamera(true)
+        executor.runAllReady()
+
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_CAMERA, TEST_UID, "", 0),
+                AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        executor.runAllReady()
+
+        verify(callback).onPrivacyItemsChanged(capture(argCaptor))
+
+        assertEquals(1, argCaptor.value.size)
+        assertEquals(PrivacyType.TYPE_CAMERA, argCaptor.value[0].privacyType)
+    }
+
+    @Test
+    fun testNotUpdated_LocationChangeWhenOnlyMicCamera() {
+        doReturn(listOf(AppOpItem(AppOpsManager.OP_COARSE_LOCATION, TEST_UID, "", 0)))
+                .`when`(appOpsController).getActiveAppOpsForUser(anyInt())
+
+        privacyItemController.addCallback(callback)
+        changeAll(false)
+        changeMicCamera(true)
+        executor.runAllReady()
+        reset(callback) // Clean callback
+
+        verify(appOpsController).addCallback(any(), capture(argCaptorCallback))
+        argCaptorCallback.value.onActiveStateChanged(
+                AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true)
+
+        verify(callback, never()).onPrivacyItemsChanged(any())
+    }
+
+    private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value)
+    private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value)
+
+    private fun changeProperty(name: String, value: Boolean?) {
+        deviceConfigProxy.setProperty(
+                DeviceConfig.NAMESPACE_PRIVACY,
+                name,
+                value?.toString(),
+                false
+        )
+    }
 }
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index bd25f2b..3ee5b28 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -25,10 +25,12 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.graphics.Point;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
+import android.view.Display;
 import android.view.MotionEvent;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -90,6 +92,8 @@
 
     private MotionEventDispatcherDelegate mMotionEventDispatcherDelegate;
     private final int mDisplayId;
+    private final Context mContext;
+    private final Point mTempPoint = new Point();
 
     private final Queue<MotionEvent> mDebugOutputEventHistory;
 
@@ -107,7 +111,7 @@
             Slog.i(LOG_TAG,
                     "WindowMagnificationGestureHandler() , displayId = " + displayId + ")");
         }
-
+        mContext = context;
         mWindowMagnificationMgr = windowMagnificationMgr;
         mDetectShortcutTrigger = detectShortcutTrigger;
         mDisplayId = displayId;
@@ -184,7 +188,14 @@
         if (!mDetectShortcutTrigger) {
             return;
         }
-        toggleMagnification(Float.NaN, Float.NaN);
+        final Point screenSize = mTempPoint;
+        getScreenSize(mTempPoint);
+        toggleMagnification(screenSize.x / 2.0f, screenSize.y / 2.0f);
+    }
+
+    private  void getScreenSize(Point outSize) {
+        final Display display = mContext.getDisplay();
+        display.getRealSize(outSize);
     }
 
     @Override
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 7cf5fd6..b7fed87 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -50,5 +50,5 @@
      * Informs battery stats of binder stats for the given work source UID.
      */
     public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount,
-            Collection<BinderCallsStats.CallStat> callStats);
+            Collection<BinderCallsStats.CallStat> callStats,  int[] binderThreadNativeTids);
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e546a28..91a1487 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1956,7 +1956,7 @@
                         nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
                     }
                     memInfo.readMemInfo();
-                    synchronized (ActivityManagerService.this) {
+                    synchronized (mProcessStats.mLock) {
                         if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in "
                                 + (SystemClock.uptimeMillis()-start) + "ms");
                         final long cachedKb = memInfo.getCachedSizeKb();
@@ -7048,9 +7048,7 @@
             mUsageStatsService.prepareShutdown();
         }
         mBatteryStatsService.shutdown();
-        synchronized (this) {
-            mProcessStats.shutdownLocked();
-        }
+        mProcessStats.shutdown();
 
         return timedout;
     }
@@ -12352,7 +12350,7 @@
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
             if (nativeProcTotalPss > 0) {
-                synchronized (this) {
+                synchronized (mProcessStats.mLock) {
                     final long cachedKb = memInfo.getCachedSizeKb();
                     final long freeKb = memInfo.getFreeSizeKb();
                     final long zramKb = memInfo.getZramTotalSizeKb();
@@ -12934,7 +12932,7 @@
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
             if (nativeProcTotalPss > 0) {
-                synchronized (this) {
+                synchronized (mProcessStats.mLock) {
                     final long cachedKb = memInfo.getCachedSizeKb();
                     final long freeKb = memInfo.getFreeSizeKb();
                     final long zramKb = memInfo.getZramTotalSizeKb();
@@ -16505,9 +16503,7 @@
         }
 
         @Override public void run() {
-            synchronized (mService) {
-                mProcessStats.writeStateAsyncLocked();
-            }
+            mProcessStats.writeStateAsync();
         }
     }
 
@@ -16557,9 +16553,13 @@
         }
         mLastMemoryLevel = memFactor;
         mLastNumProcesses = mProcessList.getLruSizeLocked();
-        boolean allChanged = mProcessStats.setMemFactorLocked(
-                memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
-        final int trackerMemFactor = mProcessStats.getMemFactorLocked();
+        boolean allChanged;
+        int trackerMemFactor;
+        synchronized (mProcessStats.mLock) {
+            allChanged = mProcessStats.setMemFactorLocked(
+                    memFactor, mAtmInternal != null ? !mAtmInternal.isSleeping() : true, now);
+            trackerMemFactor = mProcessStats.getMemFactorLocked();
+        }
         if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
             if (mLowRamStartTime == 0) {
                 mLowRamStartTime = now;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5081b36..d72998b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -227,8 +227,9 @@
 
         @Override
         public void noteBinderCallStats(int workSourceUid, long incrementatCallCount,
-                Collection<BinderCallsStats.CallStat> callStats) {
-            mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats);
+                Collection<BinderCallsStats.CallStat> callStats, int[] binderThreadNativeTids) {
+            mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats,
+                    binderThreadNativeTids);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 8970ec4..0f2dfcc 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -116,6 +116,10 @@
                 WidgetFlags.KEY_ENABLE_CURSOR_DRAG_FROM_ANYWHERE, boolean.class,
                 WidgetFlags.ENABLE_CURSOR_DRAG_FROM_ANYWHERE_DEFAULT));
         sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
+                DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL,
+                WidgetFlags.KEY_CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL, int.class,
+                WidgetFlags.CURSOR_DRAG_MIN_ANGLE_FROM_VERTICAL_DEFAULT));
+        sDeviceConfigEntries.add(new DeviceConfigEntry<Integer>(
                 DeviceConfig.NAMESPACE_WIDGET, WidgetFlags.FINGER_TO_CURSOR_DISTANCE,
                 WidgetFlags.KEY_FINGER_TO_CURSOR_DISTANCE, int.class,
                 WidgetFlags.FINGER_TO_CURSOR_DISTANCE_DEFAULT));
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f0343e1..bf15f1737 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -659,13 +659,15 @@
 
         updateUidsLocked(activeUids, nowElapsed);
 
-        if (mService.mProcessStats.shouldWriteNowLocked(now)) {
-            mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
-                    mService.mProcessStats));
-        }
+        synchronized (mService.mProcessStats.mLock) {
+            if (mService.mProcessStats.shouldWriteNowLocked(now)) {
+                mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService,
+                        mService.mProcessStats));
+            }
 
-        // Run this after making sure all procstates are updated.
-        mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+            // Run this after making sure all procstates are updated.
+            mService.mProcessStats.updateTrackingAssociationsLocked(mAdjSeq, now);
+        }
 
         if (DEBUG_OOM_ADJ) {
             final long duration = SystemClock.uptimeMillis() - now;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 1647fda..e3e1339 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -673,30 +673,33 @@
 
     public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
         if (thread == null) {
-            final ProcessState origBase = baseProcessTracker;
-            if (origBase != null) {
-                origBase.setState(ProcessStats.STATE_NOTHING,
-                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
-                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                            uid, processName, pkgList.keyAt(ipkg),
-                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                            pkgList.valueAt(ipkg).appVersion);
+            synchronized (tracker.mLock) {
+                final ProcessState origBase = baseProcessTracker;
+                if (origBase != null) {
+                    origBase.setState(ProcessStats.STATE_NOTHING,
+                            tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+                            pkgList.mPkgList);
+                    for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                                uid, processName, pkgList.keyAt(ipkg),
+                                ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                                pkgList.valueAt(ipkg).appVersion);
+                    }
+                    origBase.makeInactive();
                 }
-                origBase.makeInactive();
-            }
-            baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
-                    info.longVersionCode, processName);
-            baseProcessTracker.makeActive();
-            for (int i=0; i<pkgList.size(); i++) {
-                ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
-                if (holder.state != null && holder.state != origBase) {
-                    holder.state.makeInactive();
-                }
-                tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
+                baseProcessTracker = tracker.getProcessStateLocked(info.packageName, info.uid,
                         info.longVersionCode, processName);
-                if (holder.state != baseProcessTracker) {
-                    holder.state.makeActive();
+                baseProcessTracker.makeActive();
+                for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
+                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                    if (holder.state != null && holder.state != origBase) {
+                        holder.state.makeInactive();
+                    }
+                    tracker.updateProcessStateHolderLocked(holder, pkgList.keyAt(i), info.uid,
+                            info.longVersionCode, processName);
+                    if (holder.state != baseProcessTracker) {
+                        holder.state.makeActive();
+                    }
                 }
             }
         }
@@ -707,27 +710,30 @@
     public void makeInactive(ProcessStatsService tracker) {
         thread = null;
         mWindowProcessController.setThread(null);
-        final ProcessState origBase = baseProcessTracker;
-        if (origBase != null) {
+        synchronized (tracker.mLock) {
+            final ProcessState origBase = baseProcessTracker;
             if (origBase != null) {
-                origBase.setState(ProcessStats.STATE_NOTHING,
-                        tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList.mPkgList);
-                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                            uid, processName, pkgList.keyAt(ipkg),
-                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                            pkgList.valueAt(ipkg).appVersion);
+                if (origBase != null) {
+                    origBase.setState(ProcessStats.STATE_NOTHING,
+                            tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
+                            pkgList.mPkgList);
+                    for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                        FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                                uid, processName, pkgList.keyAt(ipkg),
+                                ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                                pkgList.valueAt(ipkg).appVersion);
+                    }
+                    origBase.makeInactive();
                 }
-                origBase.makeInactive();
-            }
-            baseProcessTracker = null;
-            for (int i=0; i<pkgList.size(); i++) {
-                ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
-                if (holder.state != null && holder.state != origBase) {
-                    holder.state.makeInactive();
+                baseProcessTracker = null;
+                for (int i = 0, ipkg = pkgList.size(); i < ipkg; i++) {
+                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                    if (holder.state != null && holder.state != origBase) {
+                        holder.state.makeInactive();
+                    }
+                    holder.pkg = null;
+                    holder.state = null;
                 }
-                holder.pkg = null;
-                holder.state = null;
             }
         }
     }
@@ -1026,17 +1032,19 @@
      */
     public boolean addPackage(String pkg, long versionCode, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
-            ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
-                    versionCode);
-            if (baseProcessTracker != null) {
-                tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
-                        processName);
-                pkgList.put(pkg, holder);
-                if (holder.state != baseProcessTracker) {
-                    holder.state.makeActive();
+            synchronized (tracker.mLock) {
+                ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+                        versionCode);
+                if (baseProcessTracker != null) {
+                    tracker.updateProcessStateHolderLocked(holder, pkg, info.uid, versionCode,
+                            processName);
+                    pkgList.put(pkg, holder);
+                    if (holder.state != baseProcessTracker) {
+                        holder.state.makeActive();
+                    }
+                } else {
+                    pkgList.put(pkg, holder);
                 }
-            } else {
-                pkgList.put(pkg, holder);
             }
             return true;
         }
@@ -1072,31 +1080,33 @@
     public void resetPackageList(ProcessStatsService tracker) {
         final int N = pkgList.size();
         if (baseProcessTracker != null) {
-            long now = SystemClock.uptimeMillis();
-            baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
-                    tracker.getMemFactorLocked(), now, pkgList.mPkgList);
-            for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
-                FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
-                        uid, processName, pkgList.keyAt(ipkg),
-                        ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
-                        pkgList.valueAt(ipkg).appVersion);
-            }
-            if (N != 1) {
-                for (int i=0; i<N; i++) {
-                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
-                    if (holder.state != null && holder.state != baseProcessTracker) {
-                        holder.state.makeInactive();
-                    }
-
+            synchronized (tracker.mLock) {
+                long now = SystemClock.uptimeMillis();
+                baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
+                        tracker.getMemFactorLocked(), now, pkgList.mPkgList);
+                for (int ipkg = pkgList.size() - 1; ipkg >= 0; ipkg--) {
+                    FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
+                            uid, processName, pkgList.keyAt(ipkg),
+                            ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
+                            pkgList.valueAt(ipkg).appVersion);
                 }
-                pkgList.clear();
-                ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
-                        info.longVersionCode);
-                tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
-                        info.longVersionCode, processName);
-                pkgList.put(info.packageName, holder);
-                if (holder.state != baseProcessTracker) {
-                    holder.state.makeActive();
+                if (N != 1) {
+                    for (int i = 0; i < N; i++) {
+                        ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                        if (holder.state != null && holder.state != baseProcessTracker) {
+                            holder.state.makeInactive();
+                        }
+
+                    }
+                    pkgList.clear();
+                    ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+                            info.longVersionCode);
+                    tracker.updateProcessStateHolderLocked(holder, info.packageName, info.uid,
+                            info.longVersionCode, processName);
+                    pkgList.put(info.packageName, holder);
+                    if (holder.state != baseProcessTracker) {
+                        holder.state.makeActive();
+                    }
                 }
             }
         } else if (N != 1) {
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index a168af5a..4e8c386 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -71,33 +71,62 @@
 
     final ActivityManagerService mAm;
     final File mBaseDir;
-    ProcessStats mProcessStats;
+
+    // Note: The locking order of the below 3 locks should be:
+    // mLock, mPendingWriteLock, mFileLock
+
+    // The lock object to protect the internal state/structures
+    final Object mLock = new Object();
+
+    // The lock object to protect the access to pending writes
+    final Object mPendingWriteLock = new Object();
+
+    // The lock object to protect the access to all of the file read/write
+    final ReentrantLock mFileLock = new ReentrantLock();
+
+    @GuardedBy("mLock")
+    final ProcessStats mProcessStats;
+
+    @GuardedBy("mFileLock")
     AtomicFile mFile;
+
+    @GuardedBy("mLock")
     boolean mCommitPending;
+
+    @GuardedBy("mLock")
     boolean mShuttingDown;
+
+    @GuardedBy("mLock")
     int mLastMemOnlyState = -1;
     boolean mMemFactorLowered;
 
-    final ReentrantLock mWriteLock = new ReentrantLock();
-    final Object mPendingWriteLock = new Object();
+    @GuardedBy("mPendingWriteLock")
     AtomicFile mPendingWriteFile;
+
+    @GuardedBy("mPendingWriteLock")
     Parcel mPendingWrite;
+
+    @GuardedBy("mPendingWriteLock")
     boolean mPendingWriteCommitted;
+
+    @GuardedBy("mLock")
     long mLastWriteTime;
 
     /** For CTS to inject the screen state. */
-    @GuardedBy("mAm")
+    @GuardedBy("mLock")
     Boolean mInjectedScreenState;
 
     public ProcessStatsService(ActivityManagerService am, File file) {
         mAm = am;
         mBaseDir = file;
         mBaseDir.mkdirs();
-        mProcessStats = new ProcessStats(true);
-        updateFile();
+        synchronized (mLock) {
+            mProcessStats = new ProcessStats(true);
+            updateFileLocked();
+        }
         SystemProperties.addChangeCallback(new Runnable() {
             @Override public void run() {
-                synchronized (mAm) {
+                synchronized (mLock) {
                     if (mProcessStats.evaluateSystemProperties(false)) {
                         mProcessStats.mFlags |= ProcessStats.FLAG_SYSPROPS;
                         writeStateLocked(true, true);
@@ -121,32 +150,33 @@
         }
     }
 
-    @GuardedBy("mAm")
-    public void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
+    @GuardedBy("mLock")
+    void updateProcessStateHolderLocked(ProcessStats.ProcessStateHolder holder,
             String packageName, int uid, long versionCode, String processName) {
         holder.pkg = mProcessStats.getPackageStateLocked(packageName, uid, versionCode);
         holder.state = mProcessStats.getProcessStateLocked(holder.pkg, processName);
     }
 
-    @GuardedBy("mAm")
-    public ProcessState getProcessStateLocked(String packageName,
+    @GuardedBy("mLock")
+    ProcessState getProcessStateLocked(String packageName,
             int uid, long versionCode, String processName) {
         return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName);
     }
 
-    @GuardedBy("mAm")
-    public ServiceState getServiceStateLocked(String packageName, int uid,
+    ServiceState getServiceState(String packageName, int uid,
             long versionCode, String processName, String className) {
-        return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
-                className);
+        synchronized (mLock) {
+            return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName,
+                    className);
+        }
     }
 
-    public boolean isMemFactorLowered() {
+    boolean isMemFactorLowered() {
         return mMemFactorLowered;
     }
 
-    @GuardedBy("mAm")
-    public boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
+    @GuardedBy("mLock")
+    boolean setMemFactorLocked(int memFactor, boolean screenOn, long now) {
         mMemFactorLowered = memFactor < mLastMemOnlyState;
         mLastMemOnlyState = memFactor;
         if (mInjectedScreenState != null) {
@@ -184,24 +214,24 @@
         return false;
     }
 
-    @GuardedBy("mAm")
-    public int getMemFactorLocked() {
+    @GuardedBy("mLock")
+    int getMemFactorLocked() {
         return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
     }
 
-    @GuardedBy("mAm")
-    public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
+    @GuardedBy("mLock")
+    void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
             long nativeMem) {
         mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
     }
 
-    @GuardedBy("mAm")
-    public void updateTrackingAssociationsLocked(int curSeq, long now) {
+    @GuardedBy("mLock")
+    void updateTrackingAssociationsLocked(int curSeq, long now) {
         mProcessStats.updateTrackingAssociationsLocked(curSeq, now);
     }
 
-    @GuardedBy("mAm")
-    public boolean shouldWriteNowLocked(long now) {
+    @GuardedBy("mLock")
+    boolean shouldWriteNowLocked(long now) {
         if (now > (mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime()
                     > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
@@ -214,25 +244,27 @@
         return false;
     }
 
-    @GuardedBy("mAm")
-    public void shutdownLocked() {
+    void shutdown() {
         Slog.w(TAG, "Writing process stats before shutdown...");
-        mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
-        writeStateSyncLocked();
-        mShuttingDown = true;
+        synchronized (mLock) {
+            mProcessStats.mFlags |= ProcessStats.FLAG_SHUTDOWN;
+            writeStateSyncLocked();
+            mShuttingDown = true;
+        }
     }
 
-    @GuardedBy("mAm")
-    public void writeStateAsyncLocked() {
-        writeStateLocked(false);
+    void writeStateAsync() {
+        synchronized (mLock) {
+            writeStateLocked(false);
+        }
     }
 
-    @GuardedBy("mAm")
-    public void writeStateSyncLocked() {
+    @GuardedBy("mLock")
+    private void writeStateSyncLocked() {
         writeStateLocked(true);
     }
 
-    @GuardedBy("mAm")
+    @GuardedBy("mLock")
     private void writeStateLocked(boolean sync) {
         if (mShuttingDown) {
             return;
@@ -242,8 +274,8 @@
         writeStateLocked(sync, commitPending);
     }
 
-    @GuardedBy("mAm")
-    public void writeStateLocked(boolean sync, final boolean commit) {
+    @GuardedBy("mLock")
+    private void writeStateLocked(boolean sync, final boolean commit) {
         final long totalTime;
         synchronized (mPendingWriteLock) {
             final long now = SystemClock.uptimeMillis();
@@ -255,13 +287,13 @@
                     mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
                 }
                 mProcessStats.writeToParcel(mPendingWrite, 0);
-                mPendingWriteFile = new AtomicFile(mFile.getBaseFile());
+                mPendingWriteFile = new AtomicFile(getCurrentFile());
                 mPendingWriteCommitted = commit;
             }
             if (commit) {
                 mProcessStats.resetSafely();
-                updateFile();
-                mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
+                updateFileLocked();
+                scheduleRequestPssAllProcs(true, false);
             }
             mLastWriteTime = SystemClock.uptimeMillis();
             totalTime = SystemClock.uptimeMillis() - now;
@@ -279,14 +311,37 @@
         performWriteState(totalTime);
     }
 
-    private void updateFile() {
-        mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
-                + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+    private void scheduleRequestPssAllProcs(boolean always, boolean memLowered) {
+        mAm.mHandler.post(() -> {
+            synchronized (mAm) {
+                mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), always, memLowered);
+            }
+        });
+    }
+
+    @GuardedBy("mLock")
+    private void updateFileLocked() {
+        mFileLock.lock();
+        try {
+            mFile = new AtomicFile(new File(mBaseDir, STATE_FILE_PREFIX
+                    + mProcessStats.mTimePeriodStartClockStr + STATE_FILE_SUFFIX));
+        } finally {
+            mFileLock.unlock();
+        }
         mLastWriteTime = SystemClock.uptimeMillis();
     }
 
-    void performWriteState(long initialTime) {
-        if (DEBUG) Slog.d(TAG, "Performing write to " + mFile.getBaseFile());
+    private File getCurrentFile() {
+        mFileLock.lock();
+        try {
+            return mFile.getBaseFile();
+        } finally {
+            mFileLock.unlock();
+        }
+    }
+
+    private void performWriteState(long initialTime) {
+        if (DEBUG) Slog.d(TAG, "Performing write to " + getCurrentFile());
         Parcel data;
         AtomicFile file;
         synchronized (mPendingWriteLock) {
@@ -298,7 +353,7 @@
             }
             mPendingWrite = null;
             mPendingWriteFile = null;
-            mWriteLock.lock();
+            mFileLock.lock();
         }
 
         final long startTime = SystemClock.uptimeMillis();
@@ -316,13 +371,13 @@
             file.failWrite(stream);
         } finally {
             data.recycle();
-            trimHistoricStatesWriteLocked();
-            mWriteLock.unlock();
+            trimHistoricStatesWriteLF();
+            mFileLock.unlock();
         }
     }
 
-    @GuardedBy("mAm")
-    boolean readLocked(ProcessStats stats, AtomicFile file) {
+    @GuardedBy("mFileLock")
+    private boolean readLF(ProcessStats stats, AtomicFile file) {
         try {
             FileInputStream stream = file.openRead();
             stats.read(stream);
@@ -387,7 +442,8 @@
         return true;
     }
 
-    private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent,
+    @GuardedBy("mFileLock")
+    private ArrayList<String> getCommittedFilesLF(int minNum, boolean inclCurrent,
             boolean inclCheckedIn) {
         File[] files = mBaseDir.listFiles();
         if (files == null || files.length <= minNum) {
@@ -414,9 +470,9 @@
         return filesArray;
     }
 
-    @GuardedBy("mAm")
-    public void trimHistoricStatesWriteLocked() {
-        ArrayList<String> filesArray = getCommittedFiles(MAX_HISTORIC_STATES, false, true);
+    @GuardedBy("mFileLock")
+    private void trimHistoricStatesWriteLF() {
+        ArrayList<String> filesArray = getCommittedFilesLF(MAX_HISTORIC_STATES, false, true);
         if (filesArray == null) {
             return;
         }
@@ -427,8 +483,8 @@
         }
     }
 
-    @GuardedBy("mAm")
-    boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
+    @GuardedBy("mLock")
+    private boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header,
             boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
             boolean sepProcStates, int[] procStates, long now, String reqPackage) {
         ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked(
@@ -502,20 +558,21 @@
         return res;
     }
 
+    @Override
     public byte[] getCurrentStats(List<ParcelFileDescriptor> historic) {
         mAm.mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
-        synchronized (mAm) {
+        synchronized (mLock) {
             long now = SystemClock.uptimeMillis();
             mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
             mProcessStats.mTimePeriodEndUptime = now;
             mProcessStats.writeToParcel(current, now, 0);
         }
-        mWriteLock.lock();
+        mFileLock.lock();
         try {
             if (historic != null) {
-                ArrayList<String> files = getCommittedFiles(0, false, true);
+                ArrayList<String> files = getCommittedFilesLF(0, false, true);
                 if (files != null) {
                     for (int i=files.size()-1; i>=0; i--) {
                         try {
@@ -529,7 +586,7 @@
                 }
             }
         } finally {
-            mWriteLock.unlock();
+            mFileLock.unlock();
         }
         return current.marshall();
     }
@@ -563,9 +620,9 @@
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
 
         long newHighWaterMark = highWaterMarkMs;
-        mWriteLock.lock();
+        mFileLock.lock();
         try {
-            ArrayList<String> files = getCommittedFiles(0, false, true);
+            ArrayList<String> files = getCommittedFilesLF(0, false, true);
             if (files != null) {
                 String highWaterMarkStr =
                         DateFormat.format("yyyy-MM-dd-HH-mm-ss", highWaterMarkMs).toString();
@@ -612,7 +669,7 @@
         } catch (IOException e) {
             Slog.w(TAG, "Failure opening procstat file", e);
         } finally {
-            mWriteLock.unlock();
+            mFileLock.unlock();
         }
         return newHighWaterMark;
     }
@@ -625,7 +682,7 @@
         return mAm.mConstants.MIN_ASSOC_LOG_DURATION;
     }
 
-    private ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
+    private static ParcelFileDescriptor protoToParcelFileDescriptor(ProcessStats stats, int section)
             throws IOException {
         final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
         Thread thr = new Thread("ProcessStats pipe output") {
@@ -645,12 +702,13 @@
         return fds[0];
     }
 
+    @Override
     public ParcelFileDescriptor getStatsOverTime(long minTime) {
         mAm.mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.PACKAGE_USAGE_STATS, null);
         Parcel current = Parcel.obtain();
         long curTime;
-        synchronized (mAm) {
+        synchronized (mLock) {
             long now = SystemClock.uptimeMillis();
             mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
             mProcessStats.mTimePeriodEndUptime = now;
@@ -658,11 +716,11 @@
             curTime = mProcessStats.mTimePeriodEndRealtime
                     - mProcessStats.mTimePeriodStartRealtime;
         }
-        mWriteLock.lock();
+        mFileLock.lock();
         try {
             if (curTime < minTime) {
                 // Need to add in older stats to reach desired time.
-                ArrayList<String> files = getCommittedFiles(0, false, true);
+                ArrayList<String> files = getCommittedFilesLF(0, false, true);
                 if (files != null && files.size() > 0) {
                     current.setDataPosition(0);
                     ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current);
@@ -673,7 +731,7 @@
                         AtomicFile file = new AtomicFile(new File(files.get(i)));
                         i--;
                         ProcessStats moreStats = new ProcessStats(false);
-                        readLocked(moreStats, file);
+                        readLF(moreStats, file);
                         if (moreStats.mReadError == null) {
                             stats.add(moreStats);
                             StringBuilder sb = new StringBuilder();
@@ -712,13 +770,14 @@
         } catch (IOException e) {
             Slog.w(TAG, "Failed building output pipe", e);
         } finally {
-            mWriteLock.unlock();
+            mFileLock.unlock();
         }
         return null;
     }
 
+    @Override
     public int getCurrentMemoryState() {
-        synchronized (mAm) {
+        synchronized (mLock) {
             return mLastMemOnlyState;
         }
     }
@@ -947,7 +1006,7 @@
                 } else if ("--current".equals(arg)) {
                     currentOnly = true;
                 } else if ("--commit".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
                         writeStateLocked(true, true);
                         pw.println("Process stats committed.");
@@ -962,29 +1021,39 @@
                     }
                     section = parseSectionOptions(args[i]);
                 } else if ("--clear".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mProcessStats.resetSafely();
-                        mAm.requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, false);
-                        ArrayList<String> files = getCommittedFiles(0, true, true);
-                        if (files != null) {
-                            for (int fi=0; fi<files.size(); fi++) {
-                                (new File(files.get(fi))).delete();
+                        scheduleRequestPssAllProcs(true, false);
+                        mFileLock.lock();
+                        try {
+                            ArrayList<String> files = getCommittedFilesLF(0, true, true);
+                            if (files != null) {
+                                for (int fi = files.size() - 1; fi >= 0; fi--) {
+                                    (new File(files.get(fi))).delete();
+                                }
                             }
+                        } finally {
+                            mFileLock.unlock();
                         }
                         pw.println("All process stats cleared.");
                         quit = true;
                     }
                 } else if ("--write".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         writeStateSyncLocked();
                         pw.println("Process stats written.");
                         quit = true;
                     }
                 } else if ("--read".equals(arg)) {
-                    synchronized (mAm) {
-                        readLocked(mProcessStats, mFile);
-                        pw.println("Process stats read.");
-                        quit = true;
+                    synchronized (mLock) {
+                        mFileLock.lock();
+                        try {
+                            readLF(mProcessStats, mFile);
+                            pw.println("Process stats read.");
+                            quit = true;
+                        } finally {
+                            mFileLock.unlock();
+                        }
                     }
                 } else if ("--start-testing".equals(arg)) {
                     synchronized (mAm) {
@@ -999,17 +1068,17 @@
                         quit = true;
                     }
                 } else if ("--pretend-screen-on".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mInjectedScreenState = true;
                     }
                     quit = true;
                 } else if ("--pretend-screen-off".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mInjectedScreenState = false;
                     }
                     quit = true;
                 } else if ("--stop-pretend-screen".equals(arg)) {
-                    synchronized (mAm) {
+                    synchronized (mLock) {
                         mInjectedScreenState = null;
                     }
                     quit = true;
@@ -1060,7 +1129,7 @@
                 }
             }
             pw.println();
-            synchronized (mAm) {
+            synchronized (mLock) {
                 dumpFilteredProcessesCsvLocked(pw, null,
                         csvSepScreenStats, csvScreenStats, csvSepMemStats, csvMemStats,
                         csvSepProcStats, csvProcStats, now, reqPackage);
@@ -1090,14 +1159,26 @@
             return;
         } else if (lastIndex > 0) {
             pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
-            ArrayList<String> files = getCommittedFiles(0, false, true);
-            if (lastIndex >= files.size()) {
-                pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
-                return;
+
+            ArrayList<String> files;
+            AtomicFile file;
+            ProcessStats processStats;
+
+            mFileLock.lock();
+            try {
+                files = getCommittedFilesLF(0, false, true);
+                if (lastIndex >= files.size()) {
+                    pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
+                    return;
+                }
+                file = new AtomicFile(new File(files.get(lastIndex)));
+                processStats = new ProcessStats(false);
+                readLF(processStats, file);
+            } finally {
+                mFileLock.unlock();
             }
-            AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
-            ProcessStats processStats = new ProcessStats(false);
-            readLocked(processStats, file);
+
+            // No lock is needed now, since only us have the access to the 'processStats'.
             if (processStats.mReadError != null) {
                 if (isCheckin || isCompact) pw.print("err,");
                 pw.print("Failure reading "); pw.print(files.get(lastIndex));
@@ -1118,7 +1199,7 @@
                     processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
                             dumpAll, activeOnly, section);
                     if (dumpAll) {
-                        pw.print("  mFile="); pw.println(mFile.getBaseFile());
+                        pw.print("  mFile="); pw.println(getCurrentFile());
                     }
                 } else {
                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1129,9 +1210,9 @@
 
         boolean sepNeeded = false;
         if ((dumpAll || isCheckin) && !currentOnly) {
-            mWriteLock.lock();
+            mFileLock.lock();
             try {
-                ArrayList<String> files = getCommittedFiles(0, false, !isCheckin);
+                ArrayList<String> files = getCommittedFilesLF(0, false, !isCheckin);
                 if (files != null) {
                     int start = isCheckin ? 0 : (files.size() - maxNum);
                     if (start < 0) {
@@ -1142,7 +1223,7 @@
                         try {
                             AtomicFile file = new AtomicFile(new File(files.get(i)));
                             ProcessStats processStats = new ProcessStats(false);
-                            readLocked(processStats, file);
+                            readLF(processStats, file);
                             if (processStats.mReadError != null) {
                                 if (isCheckin || isCompact) pw.print("err,");
                                 pw.print("Failure reading "); pw.print(files.get(i));
@@ -1188,11 +1269,11 @@
                     }
                 }
             } finally {
-                mWriteLock.unlock();
+                mFileLock.unlock();
             }
         }
         if (!isCheckin) {
-            synchronized (mAm) {
+            synchronized (mLock) {
                 if (isCompact) {
                     mProcessStats.dumpCheckinLocked(pw, reqPackage, section);
                 } else {
@@ -1204,7 +1285,7 @@
                         mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
                                 dumpAll, activeOnly, section);
                         if (dumpAll) {
-                            pw.print("  mFile="); pw.println(mFile.getBaseFile());
+                            pw.print("  mFile="); pw.println(getCurrentFile());
                         }
                     } else {
                         mProcessStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
@@ -1249,7 +1330,7 @@
 
         // dump current procstats
         long now;
-        synchronized (mAm) {
+        synchronized (mLock) {
             now = SystemClock.uptimeMillis();
             final long token = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW);
             mProcessStats.dumpDebug(proto, now, ProcessStats.REPORT_ALL);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 022b04d..4a27030 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -528,8 +528,9 @@
             return tracker;
         }
         if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
-            tracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
-                    serviceInfo.applicationInfo.uid, serviceInfo.applicationInfo.longVersionCode,
+            tracker = ams.mProcessStats.getServiceState(serviceInfo.packageName,
+                    serviceInfo.applicationInfo.uid,
+                    serviceInfo.applicationInfo.longVersionCode,
                     serviceInfo.processName, serviceInfo.name);
             tracker.applyNewOwner(this);
         }
@@ -546,7 +547,8 @@
     public void makeRestarting(int memFactor, long now) {
         if (restartTracker == null) {
             if ((serviceInfo.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) == 0) {
-                restartTracker = ams.mProcessStats.getServiceStateLocked(serviceInfo.packageName,
+                restartTracker = ams.mProcessStats.getServiceState(
+                        serviceInfo.packageName,
                         serviceInfo.applicationInfo.uid,
                         serviceInfo.applicationInfo.longVersionCode,
                         serviceInfo.processName, serviceInfo.name);
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index e17cca4..cea5a69 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -88,7 +88,7 @@
 
     // Default time limit in milliseconds for the ConnectivityManager to find a suitable
     // network with SUPL connectivity or report an error.
-    private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 10 * 1000;
+    private static final int SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS = 20 * 1000;
 
     private static final int HASH_MAP_INITIAL_CAPACITY_TO_TRACK_CONNECTED_NETWORKS = 5;
 
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 249b6801..07527c2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -270,11 +270,12 @@
     abstract boolean revertActiveSessions();
 
     /**
-     * Abandons the staged session with the given sessionId.
+     * Abandons the staged session with the given sessionId. Client should handle {@code false}
+     * return value carefully as failure here can leave device in inconsistent state.
      *
-     * @return {@code true} upon success, {@code false} if any remote exception occurs
+     * @return {@code true} upon success, {@code false} if any exception occurs
      */
-    abstract boolean abortStagedSession(int sessionId) throws PackageManagerException;
+    abstract boolean abortStagedSession(int sessionId);
 
     /**
      * Uninstalls given {@code apexPackage}.
@@ -753,17 +754,13 @@
         }
 
         @Override
-        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+        boolean abortStagedSession(int sessionId) {
             try {
                 waitForApexService().abortStagedSession(sessionId);
                 return true;
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Unable to contact apexservice", re);
-                return false;
             } catch (Exception e) {
-                throw new PackageManagerException(
-                        PackageInstaller.SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                        "Failed to abort staged session : " + e.getMessage());
+                Slog.e(TAG, e.getMessage(), e);
+                return false;
             }
         }
 
@@ -1122,7 +1119,7 @@
         }
 
         @Override
-        boolean abortStagedSession(int sessionId) throws PackageManagerException {
+        boolean abortStagedSession(int sessionId) {
             throw new UnsupportedOperationException();
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index aa2b125..05026a0 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2091,7 +2091,7 @@
         if (ps == null) {
             return 0;
         }
-        final File apkDirOrPath = ps.codePath;
+        final File apkDirOrPath = ps.getCodePath();
         if (apkDirOrPath == null) {
             return 0;
         }
@@ -3321,7 +3321,8 @@
     /** {@hide} */
     void setStagedSessionReady() {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = true;
             mStagedSessionApplied = false;
             mStagedSessionFailed = false;
@@ -3332,10 +3333,10 @@
     }
 
     /** {@hide} */
-    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode,
-                                String errorMessage) {
+    void setStagedSessionFailed(@StagedSessionErrorCode int errorCode, String errorMessage) {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = false;
             mStagedSessionApplied = false;
             mStagedSessionFailed = true;
@@ -3350,7 +3351,8 @@
     /** {@hide} */
     void setStagedSessionApplied() {
         synchronized (mLock) {
-            if (mDestroyed) return; // Do not allow destroyed staged session to change state
+            // Do not allow destroyed/failed staged session to change state
+            if (mDestroyed || mStagedSessionFailed) return;
             mStagedSessionReady = false;
             mStagedSessionApplied = true;
             mStagedSessionFailed = false;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a726c8d..58a1648 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3010,7 +3010,7 @@
             final int packageSettingCount = mSettings.mPackages.size();
             for (int i = packageSettingCount - 1; i >= 0; i--) {
                 PackageSetting ps = mSettings.mPackages.valueAt(i);
-                if (!isExternal(ps) && (ps.codePath == null || !ps.codePath.exists())
+                if (!isExternal(ps) && (ps.getCodePath() == null || !ps.getCodePath().exists())
                         && mSettings.getDisabledSystemPkgLPr(ps.name) != null) {
                     mSettings.mPackages.removeAt(i);
                     mSettings.enableSystemPackageLPw(ps.name);
@@ -3175,11 +3175,11 @@
                             logCriticalInfo(Log.WARN,
                                     "Expecting better updated system app for " + ps.name
                                     + "; removing system app.  Last known"
-                                    + " codePath=" + ps.codePathString
+                                    + " codePath=" + ps.getCodePathString()
                                     + ", versionCode=" + ps.versionCode
                                     + "; scanned versionCode=" + scannedPkg.getLongVersionCode());
                             removePackageLI(scannedPkg, true);
-                            mExpectingBetter.put(ps.name, ps.codePath);
+                            mExpectingBetter.put(ps.name, ps.getCodePath());
                         }
 
                         continue;
@@ -3202,14 +3202,14 @@
                         // code path, but, changes the package name.
                         final PackageSetting disabledPs =
                                 mSettings.getDisabledSystemPkgLPr(ps.name);
-                        if (disabledPs.codePath == null || !disabledPs.codePath.exists()
+                        if (disabledPs.getCodePath() == null || !disabledPs.getCodePath().exists()
                                 || disabledPs.pkg == null) {
                             possiblyDeletedUpdatedSystemApps.add(ps.name);
                         } else {
                             // We're expecting that the system app should remain disabled, but add
                             // it to expecting better to recover in case the data version cannot
                             // be scanned.
-                            mExpectingBetter.put(disabledPs.name, disabledPs.codePath);
+                            mExpectingBetter.put(disabledPs.name, disabledPs.getCodePath());
                         }
                     }
                 }
@@ -8551,6 +8551,15 @@
             if (listUninstalled) {
                 list = new ArrayList<>(mSettings.mPackages.size());
                 for (PackageSetting ps : mSettings.mPackages.values()) {
+                    if (listFactory) {
+                        if (!ps.isSystem()) {
+                            continue;
+                        }
+                        PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+                        if (psDisabled != null) {
+                            ps = psDisabled;
+                        }
+                    }
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
@@ -8565,7 +8574,16 @@
             } else {
                 list = new ArrayList<>(mPackages.size());
                 for (AndroidPackage p : mPackages.values()) {
-                    final PackageSetting ps = getPackageSetting(p.getPackageName());
+                    PackageSetting ps = getPackageSetting(p.getPackageName());
+                    if (listFactory) {
+                        if (!p.isSystem()) {
+                            continue;
+                        }
+                        PackageSetting psDisabled = mSettings.getDisabledSystemPkgLPr(ps);
+                        if (psDisabled != null) {
+                            ps = psDisabled;
+                        }
+                    }
                     if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) {
                         continue;
                     }
@@ -9150,7 +9168,7 @@
                 : getLastModifiedTime(parsedPackage);
         final VersionInfo settingsVersionForPackage = getSettingsVersionForPackage(parsedPackage);
         if (ps != null && !forceCollect
-                && ps.codePathString.equals(parsedPackage.getCodePath())
+                && ps.getCodePathString().equals(parsedPackage.getCodePath())
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(settingsVersionForPackage)
                 && !isRecoverSignatureUpdateNeeded(settingsVersionForPackage)) {
@@ -9383,8 +9401,8 @@
             }
         }
 
-        final boolean newPkgChangedPaths =
-                pkgAlreadyExists && !pkgSetting.codePathString.equals(parsedPackage.getCodePath());
+        final boolean newPkgChangedPaths = pkgAlreadyExists
+                && !pkgSetting.getCodePathString().equals(parsedPackage.getCodePath());
         final boolean newPkgVersionGreater =
                 pkgAlreadyExists && parsedPackage.getLongVersionCode() > pkgSetting.versionCode;
         final boolean isSystemPkgBetter = scanSystemPartition && isSystemPkgUpdated
@@ -9403,11 +9421,11 @@
                     "System package updated;"
                     + " name: " + pkgSetting.name
                     + "; " + pkgSetting.versionCode + " --> " + parsedPackage.getLongVersionCode()
-                    + "; " + pkgSetting.codePathString + " --> " + parsedPackage.getCodePath());
+                    + "; " + pkgSetting.getCodePathString()
+                            + " --> " + parsedPackage.getCodePath());
 
             final InstallArgs args = createInstallArgsForExisting(
-                    pkgSetting.codePathString,
-                    pkgSetting.resourcePathString, getAppDexInstructionSets(
+                    pkgSetting.getCodePathString(), getAppDexInstructionSets(
                             pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
             args.cleanUpResourcesLI();
             synchronized (mLock) {
@@ -9482,11 +9500,10 @@
                                 + " name: " + pkgSetting.name
                                 + "; " + pkgSetting.versionCode + " --> "
                                 + parsedPackage.getLongVersionCode()
-                                + "; " + pkgSetting.codePathString + " --> "
+                                + "; " + pkgSetting.getCodePathString() + " --> "
                                 + parsedPackage.getCodePath());
                 InstallArgs args = createInstallArgsForExisting(
-                        pkgSetting.codePathString,
-                        pkgSetting.resourcePathString, getAppDexInstructionSets(
+                        pkgSetting.getCodePathString(), getAppDexInstructionSets(
                                 pkgSetting.primaryCpuAbiString, pkgSetting.secondaryCpuAbiString));
                 synchronized (mInstallLock) {
                     args.cleanUpResourcesLI();
@@ -9499,7 +9516,7 @@
                 logCriticalInfo(Log.INFO,
                         "System package disabled;"
                                 + " name: " + pkgSetting.name
-                                + "; old: " + pkgSetting.codePathString + " @ "
+                                + "; old: " + pkgSetting.getCodePathString() + " @ "
                                 + pkgSetting.versionCode
                                 + "; new: " + parsedPackage.getCodePath() + " @ "
                                 + parsedPackage.getCodePath());
@@ -11325,7 +11342,7 @@
                         if (changedAbiCodePath == null) {
                             changedAbiCodePath = new ArrayList<>();
                         }
-                        changedAbiCodePath.add(ps.codePathString);
+                        changedAbiCodePath.add(ps.getCodePathString());
                     }
                 }
             }
@@ -11421,7 +11438,6 @@
 
         // Initialize package source and resource directories
         final File destCodeFile = new File(parsedPackage.getCodePath());
-        final File destResourceFile = new File(parsedPackage.getCodePath());
 
         // We keep references to the derived CPU Abis from settings in oder to reuse
         // them in the case where we're not upgrading or booting for the first time.
@@ -11479,7 +11495,7 @@
             // REMOVE SharedUserSetting from method; update in a separate call
             pkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),
                     originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,
-                    destCodeFile, destResourceFile, parsedPackage.getNativeLibraryRootDir(),
+                    destCodeFile, parsedPackage.getNativeLibraryRootDir(),
                     AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),
                     AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
                     parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,
@@ -11497,7 +11513,7 @@
             // secondaryCpuAbi are not known at this point so we always update them
             // to null here, only to reset them at a later point.
             Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
-                    destCodeFile, destResourceFile, parsedPackage.getNativeLibraryDir(),
+                    destCodeFile, parsedPackage.getNativeLibraryDir(),
                     AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),
                     AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),
                     PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
@@ -12066,15 +12082,13 @@
                     if (known != null) {
                         if (DEBUG_PACKAGE_SCANNING) {
                             Log.d(TAG, "Examining " + pkg.getCodePath()
-                                    + " and requiring known paths " + known.codePathString
-                                    + " & " + known.resourcePathString);
+                                    + " and requiring known path " + known.getCodePathString());
                         }
-                        if (!pkg.getCodePath().equals(known.codePathString)
-                                || !pkg.getCodePath().equals(known.resourcePathString)) {
+                        if (!pkg.getCodePath().equals(known.getCodePathString())) {
                             throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
                                     "Application package " + pkg.getPackageName()
                                     + " found at " + pkg.getCodePath()
-                                    + " but expected at " + known.codePathString
+                                    + " but expected at " + known.getCodePathString()
                                     + "; ignoring.");
                         }
                     } else {
@@ -15586,9 +15600,8 @@
      * Create args that describe an existing installed package. Typically used
      * when cleaning up old installs, or used as a move source.
      */
-    private InstallArgs createInstallArgsForExisting(String codePath,
-            String resourcePath, String[] instructionSets) {
-        return new FileInstallArgs(codePath, resourcePath, instructionSets);
+    private InstallArgs createInstallArgsForExisting(String codePath, String[] instructionSets) {
+        return new FileInstallArgs(codePath, instructionSets);
     }
 
     static abstract class InstallArgs {
@@ -15669,10 +15682,8 @@
         abstract boolean doRename(int status, ParsedPackage parsedPackage);
         abstract int doPostInstall(int status, int uid);
 
-        /** @see PackageSettingBase#codePathString */
+        /** @see PackageSettingBase#getCodePath() */
         abstract String getCodePath();
-        /** @see PackageSettingBase#resourcePathString */
-        abstract String getResourcePath();
 
         // Need installer lock especially for dex file removal.
         abstract void cleanUpResourcesLI();
@@ -15743,14 +15754,13 @@
         }
 
         /** Existing install */
-        FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
+        FileInstallArgs(String codePath, String[] instructionSets) {
             super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                     null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
                     PackageParser.SigningDetails.UNKNOWN,
                     PackageManager.INSTALL_REASON_UNKNOWN, false,
                     DataLoaderType.NONE);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
-            this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
         }
 
         int copyApk() {
@@ -15766,7 +15776,6 @@
             if (origin.staged) {
                 if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                 codeFile = origin.file;
-                resourceFile = origin.file;
                 return PackageManager.INSTALL_SUCCEEDED;
             }
 
@@ -15775,7 +15784,6 @@
                 final File tempDir =
                         mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
                 codeFile = tempDir;
-                resourceFile = tempDir;
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to create copy file: " + e);
                 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -15846,7 +15854,6 @@
 
             // Reflect the rename internally
             codeFile = afterCodeFile;
-            resourceFile = afterCodeFile;
 
             // Reflect the rename in scanned details
             try {
@@ -15875,11 +15882,6 @@
             return (codeFile != null) ? codeFile.getAbsolutePath() : null;
         }
 
-        @Override
-        String getResourcePath() {
-            return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
-        }
-
         private boolean cleanUp() {
             if (codeFile == null || !codeFile.exists()) {
                 return false;
@@ -15892,10 +15894,6 @@
 
             removeCodePathLI(codeFile);
 
-            if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
-                resourceFile.delete();
-            }
-
             return true;
         }
 
@@ -15927,7 +15925,6 @@
      */
     class MoveInstallArgs extends InstallArgs {
         private File codeFile;
-        private File resourceFile;
 
         /** New install */
         MoveInstallArgs(InstallParams params) {
@@ -15950,7 +15947,6 @@
 
             final String toPathName = new File(move.fromCodePath).getName();
             codeFile = new File(Environment.getDataAppDirectory(move.toUuid), toPathName);
-            resourceFile = codeFile;
             if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + codeFile);
 
             return PackageManager.INSTALL_SUCCEEDED;
@@ -15987,11 +15983,6 @@
             return (codeFile != null) ? codeFile.getAbsolutePath() : null;
         }
 
-        @Override
-        String getResourcePath() {
-            return (resourceFile != null) ? resourceFile.getAbsolutePath() : null;
-        }
-
         private boolean cleanUp(String volumeUuid) {
             final String toPathName = new File(move.fromCodePath).getName();
             final File codeFile = new File(Environment.getDataAppDirectory(volumeUuid),
@@ -16777,7 +16768,6 @@
                         // installed.  We need to make sure to delete the older one's .apk.
                         res.removedInfo.args = createInstallArgsForExisting(
                                 oldPackage.getCodePath(),
-                                oldPackage.getCodePath(),
                                 getAppDexInstructionSets(
                                         AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
                                                 deletedPkgSetting),
@@ -18558,7 +18548,6 @@
         // user handle installed state
         int[] allUsers;
         /** enabled state of the uninstalled application */
-        final int origEnabledState;
         synchronized (mLock) {
             uninstalledPs = mSettings.mPackages.get(packageName);
             if (uninstalledPs == null) {
@@ -18574,10 +18563,6 @@
             }
 
             disabledSystemPs = mSettings.getDisabledSystemPkgLPr(packageName);
-            // Save the enabled state before we delete the package. When deleting a stub
-            // application we always set the enabled state to 'disabled'.
-            origEnabledState = uninstalledPs == null
-                    ? COMPONENT_ENABLED_STATE_DEFAULT : uninstalledPs.getEnabled(userId);
             // Static shared libs can be declared by any package, so let us not
             // allow removing a package if it provides a lib others depend on.
             pkg = mPackages.get(packageName);
@@ -18656,20 +18641,32 @@
             if (stubPkg != null && stubPkg.isStub()) {
                 final PackageSetting stubPs;
                 synchronized (mLock) {
-                    // restore the enabled state of the stub; the state is overwritten when
-                    // the stub is uninstalled
                     stubPs = mSettings.getPackageLPr(stubPkg.getPackageName());
-                    if (stubPs != null) {
-                        stubPs.setEnabled(origEnabledState, userId, "android");
-                    }
                 }
-                if (origEnabledState == COMPONENT_ENABLED_STATE_DEFAULT
-                        || origEnabledState == COMPONENT_ENABLED_STATE_ENABLED) {
-                    if (DEBUG_COMPRESSION) {
-                        Slog.i(TAG, "Enabling system stub after removal; pkg: "
-                                + stubPkg.getPackageName());
+
+                if (stubPs != null) {
+                    boolean enable = false;
+                    for (int aUserId : allUsers) {
+                        if (stubPs.getInstalled(aUserId)) {
+                            int enabled = stubPs.getEnabled(aUserId);
+                            if (enabled == COMPONENT_ENABLED_STATE_DEFAULT
+                                    || enabled == COMPONENT_ENABLED_STATE_ENABLED) {
+                                enable = true;
+                                break;
+                            }
+                        }
                     }
-                    enableCompressedPackage(stubPkg, stubPs);
+
+                    if (enable) {
+                        if (DEBUG_COMPRESSION) {
+                            Slog.i(TAG, "Enabling system stub after removal; pkg: "
+                                    + stubPkg.getPackageName());
+                        }
+                        enableCompressedPackage(stubPkg, stubPs);
+                    } else if (DEBUG_COMPRESSION) {
+                        Slog.i(TAG, "System stub disabled for all users, leaving uncompressed "
+                                + "after removal; pkg: " + stubPkg.getPackageName());
+                    }
                 }
             }
         }
@@ -18998,7 +18995,7 @@
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
         try {
-            installPackageFromSystemLIF(disabledPs.codePathString, allUserHandles,
+            installPackageFromSystemLIF(disabledPs.getCodePathString(), allUserHandles,
                     outInfo == null ? null : outInfo.origUsers, deletedPs.getPermissionsState(),
                     writeSettings);
         } catch (PackageManagerException e) {
@@ -19013,8 +19010,15 @@
                 // and re-enable it afterward.
                 final PackageSetting stubPs = mSettings.mPackages.get(deletedPkg.getPackageName());
                 if (stubPs != null) {
-                    stubPs.setEnabled(
-                            COMPONENT_ENABLED_STATE_DISABLED, UserHandle.USER_SYSTEM, "android");
+                    int userId = action.user == null
+                            ? UserHandle.USER_ALL : action.user.getIdentifier();
+                    if (userId == UserHandle.USER_ALL) {
+                        for (int aUserId : allUserHandles) {
+                            stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, aUserId, "android");
+                        }
+                    } else if (userId >= UserHandle.USER_SYSTEM) {
+                        stubPs.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, userId, "android");
+                    }
                 }
             }
         }
@@ -19123,7 +19127,7 @@
         // Delete application code and resources only for parent packages
         if (deleteCodeAndResources && (outInfo != null)) {
             outInfo.args = createInstallArgsForExisting(
-                    ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(
+                    ps.getCodePathString(), getAppDexInstructionSets(
                             ps.primaryCpuAbiString, ps.secondaryCpuAbiString));
             if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args);
         }
@@ -19660,7 +19664,7 @@
 
         final String[] packageNames = { packageName };
         final long[] ceDataInodes = { ps.getCeDataInode(userId) };
-        final String[] codePaths = { ps.codePathString };
+        final String[] codePaths = { ps.getCodePathString() };
 
         try {
             mInstaller.getAppSize(ps.volumeUuid, packageNames, userId, 0,
@@ -22582,11 +22586,11 @@
             synchronized (mInstallLock) {
                 final AndroidPackage pkg;
                 try {
-                    pkg = scanPackageTracedLI(ps.codePath, parseFlags, SCAN_INITIAL, 0, null);
+                    pkg = scanPackageTracedLI(ps.getCodePath(), parseFlags, SCAN_INITIAL, 0, null);
                     loaded.add(pkg);
 
                 } catch (PackageManagerException e) {
-                    Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
+                    Slog.w(TAG, "Failed to scan " + ps.getCodePath() + ": " + e.getMessage());
                 }
 
                 if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
@@ -22657,33 +22661,33 @@
 
         final ArrayList<AndroidPackage> unloaded = new ArrayList<>();
         synchronized (mInstallLock) {
-        synchronized (mLock) {
-            final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
-            for (PackageSetting ps : packages) {
-                if (ps.pkg == null) continue;
+            synchronized (mLock) {
+                final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(volumeUuid);
+                for (PackageSetting ps : packages) {
+                    if (ps.pkg == null) continue;
 
-                final AndroidPackage pkg = ps.pkg;
-                final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
-                final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
+                    final AndroidPackage pkg = ps.pkg;
+                    final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
+                    final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
 
-                try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
-                        "unloadPrivatePackagesInner")) {
-                    if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
-                            false, null)) {
-                        unloaded.add(pkg);
-                    } else {
-                        Slog.w(TAG, "Failed to unload " + ps.codePath);
+                    try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
+                            "unloadPrivatePackagesInner")) {
+                        if (deletePackageLIF(ps.name, null, false, null, deleteFlags, outInfo,
+                                false, null)) {
+                            unloaded.add(pkg);
+                        } else {
+                            Slog.w(TAG, "Failed to unload " + ps.getCodePath());
+                        }
                     }
+
+                    // Try very hard to release any references to this package
+                    // so we don't risk the system server being killed due to
+                    // open FDs
+                    AttributeCache.instance().removePackage(ps.name);
                 }
 
-                // Try very hard to release any references to this package
-                // so we don't risk the system server being killed due to
-                // open FDs
-                AttributeCache.instance().removePackage(ps.name);
+                mSettings.writeLPr();
             }
-
-            mSettings.writeLPr();
-        }
         }
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
@@ -22726,7 +22730,7 @@
             final int packageCount = mSettings.mPackages.size();
             for (int i = 0; i < packageCount; i++) {
                 final PackageSetting ps = mSettings.mPackages.valueAt(i);
-                codePaths.add(ps.codePath.getAbsolutePath());
+                codePaths.add(ps.getCodePath().getAbsolutePath());
             }
             return codePaths;
         }
@@ -23643,8 +23647,20 @@
         }
     }
 
-    void onNewUserCreated(final int userId) {
-        mPermissionManager.onNewUserCreated(userId);
+    void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) {
+        if (DEBUG_PERMISSIONS) {
+            Slog.d(TAG, "onNewUserCreated(id=" + userId
+                    + ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
+        }
+        if (!convertedFromPreCreated) {
+            mPermissionManager.onNewUserCreated(userId);
+            return;
+        }
+        if (!readPermissionStateForUser(userId)) {
+            // Could not read the existing permissions, re-grant them.
+            Slog.i(TAG, "re-granting permissions for pre-created user " + userId);
+            mPermissionManager.onNewUserCreated(userId);
+        }
     }
 
     boolean readPermissionStateForUser(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 668f375..7aeec6d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -423,13 +423,15 @@
             final List<ApplicationInfo> list;
             if (packageName == null) {
                 final ParceledListSlice<ApplicationInfo> packages =
-                        mInterface.getInstalledApplications(
-                                PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+                        mInterface.getInstalledApplications(PackageManager.MATCH_SYSTEM_ONLY
+                                        | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                                UserHandle.USER_SYSTEM);
                 list = packages.getList();
             } else {
                 list = new ArrayList<>(1);
-                list.add(mInterface.getApplicationInfo(packageName,
-                        PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
+                list.add(mInterface.getApplicationInfo(packageName, PackageManager.MATCH_SYSTEM_ONLY
+                                | PackageManager.MATCH_UNINSTALLED_PACKAGES,
+                        UserHandle.USER_SYSTEM));
             }
             for (ApplicationInfo info : list) {
                 if (info.isUpdatedSystemApp()) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 432d7f3..a3a7273 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -71,13 +71,13 @@
     private PackageStateUnserialized pkgState = new PackageStateUnserialized();
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
-    public PackageSetting(String name, String realName, File codePath, File resourcePath,
+    public PackageSetting(String name, String realName, @NonNull File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int privateFlags,
             int sharedUserId, String[] usesStaticLibraries,
             long[] usesStaticLibrariesVersions, Map<String, ArraySet<String>> mimeGroups) {
-        super(name, realName, codePath, resourcePath, legacyNativeLibraryPathString,
+        super(name, realName, codePath, legacyNativeLibraryPathString,
                 primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                 pVersionCode, pkgFlags, privateFlags,
                 usesStaticLibraries, usesStaticLibrariesVersions);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 834303c..6010344 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -64,10 +64,8 @@
      * this is path to single base APK file; for cluster packages this is
      * path to the cluster directory.
      */
-    File codePath;
-    String codePathString;
-    File resourcePath;
-    String resourcePathString;
+    private File mCodePath;
+    private String mCodePathString;
 
     String[] usesStaticLibraries;
     long[] usesStaticLibrariesVersions;
@@ -138,7 +136,7 @@
 
     boolean forceQueryableOverride;
 
-    PackageSettingBase(String name, String realName, File codePath, File resourcePath,
+    PackageSettingBase(String name, String realName, @NonNull File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
             long pVersionCode, int pkgFlags, int pkgPrivateFlags,
@@ -148,10 +146,7 @@
         this.realName = realName;
         this.usesStaticLibraries = usesStaticLibraries;
         this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
-        this.codePath = codePath;
-        this.codePathString = codePath.toString();
-        this.resourcePath = resourcePath;
-        this.resourcePathString = resourcePath.toString();
+        setCodePath(codePath);
         this.legacyNativeLibraryPathString = legacyNativeLibraryPathString;
         this.primaryCpuAbiString = primaryCpuAbiString;
         this.secondaryCpuAbiString = secondaryCpuAbiString;
@@ -235,8 +230,7 @@
     }
 
     private void doCopy(PackageSettingBase orig) {
-        codePath = orig.codePath;
-        codePathString = orig.codePathString;
+        setCodePath(orig.getCodePath());
         cpuAbiOverrideString = orig.cpuAbiOverrideString;
         firstInstallTime = orig.firstInstallTime;
         installPermissionsFixed = orig.installPermissionsFixed;
@@ -246,8 +240,6 @@
         legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
         // Intentionally skip mOldCodePaths; it's not relevant for copies
         primaryCpuAbiString = orig.primaryCpuAbiString;
-        resourcePath = orig.resourcePath;
-        resourcePathString = orig.resourcePathString;
         secondaryCpuAbiString = orig.secondaryCpuAbiString;
         signatures = orig.signatures;
         timeStamp = orig.timeStamp;
@@ -705,6 +697,20 @@
         return userState.harmfulAppWarning;
     }
 
+    PackageSettingBase setCodePath(@NonNull File codePath) {
+        this.mCodePath = codePath;
+        this.mCodePathString = codePath.toString();
+        return this;
+    }
+
+    File getCodePath() {
+        return mCodePath;
+    }
+
+    String getCodePathString() {
+        return mCodePathString;
+    }
+
     /**
      * @see PackageUserState#overrideLabelAndIcon(ComponentName, String, Integer)
      *
@@ -727,10 +733,7 @@
 
     protected PackageSettingBase updateFrom(PackageSettingBase other) {
         super.copyFrom(other);
-        this.codePath = other.codePath;
-        this.codePathString = other.codePathString;
-        this.resourcePath = other.resourcePath;
-        this.resourcePathString = other.resourcePathString;
+        setCodePath(other.getCodePath());
         this.usesStaticLibraries = other.usesStaticLibraries;
         this.usesStaticLibrariesVersions = other.usesStaticLibrariesVersions;
         this.legacyNativeLibraryPathString = other.legacyNativeLibraryPathString;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 1805713..3e3e3c5 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -538,7 +538,7 @@
             return null;
         }
         p.getPkgState().setUpdatedSystemApp(false);
-        PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
+        PackageSetting ret = addPackageLPw(name, p.realName, p.getCodePath(),
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
                 p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
@@ -558,7 +558,7 @@
         mDisabledSysPackages.remove(name);
     }
 
-    PackageSetting addPackageLPw(String name, String realName, File codePath, File resourcePath,
+    PackageSetting addPackageLPw(String name, String realName, File codePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
             pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
@@ -572,10 +572,9 @@
                     "Adding duplicate package, keeping first: " + name);
             return null;
         }
-        p = new PackageSetting(name, realName, codePath, resourcePath,
-                legacyNativeLibraryPathString, primaryCpuAbiString, secondaryCpuAbiString,
-                cpuAbiOverrideString, vc, pkgFlags, pkgPrivateFlags,
-                0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
+        p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
+                primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
+                pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
                 mimeGroups);
         p.appId = uid;
         if (registerExistingAppIdLPw(uid, p, name)) {
@@ -635,7 +634,7 @@
      */
     static @NonNull PackageSetting createNewSetting(String pkgName, PackageSetting originalPkg,
             PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
-            File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
+            File codePath, String legacyNativeLibraryPath, String primaryCpuAbi,
             String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
             UserHandle installUser, boolean allowInstall, boolean instantApp,
             boolean virtualPreload, UserManagerService userManager,
@@ -646,12 +645,11 @@
             if (PackageManagerService.DEBUG_UPGRADE) Log.v(PackageManagerService.TAG, "Package "
                     + pkgName + " is adopting original package " + originalPkg.name);
             pkgSetting = new PackageSetting(originalPkg, pkgName /*realPkgName*/);
-            pkgSetting.codePath = codePath;
+            pkgSetting.setCodePath(codePath);
             pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
             pkgSetting.pkgFlags = pkgFlags;
             pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
             pkgSetting.primaryCpuAbiString = primaryCpuAbi;
-            pkgSetting.resourcePath = resourcePath;
             pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
             // NOTE: Create a deeper copy of the package signatures so we don't
             // overwrite the signatures in the original package setting.
@@ -662,7 +660,7 @@
             // Update new package state.
             pkgSetting.setTimeStamp(codePath.lastModified());
         } else {
-            pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, resourcePath,
+            pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
                     legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
                     null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
                     0 /*sharedUserId*/, usesStaticLibraries,
@@ -756,10 +754,9 @@
      */
     static void updatePackageSetting(@NonNull PackageSetting pkgSetting,
             @Nullable PackageSetting disabledPkg, @Nullable SharedUserSetting sharedUser,
-            @NonNull File codePath, File resourcePath,
-            @Nullable String legacyNativeLibraryPath, @Nullable String primaryCpuAbi,
-            @Nullable String secondaryCpuAbi, int pkgFlags, int pkgPrivateFlags,
-            @NonNull UserManagerService userManager,
+            @NonNull File codePath, @Nullable String legacyNativeLibraryPath,
+            @Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
+            int pkgPrivateFlags, @NonNull UserManagerService userManager,
             @Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
             @Nullable Set<String> mimeGroupNames)
                     throws PackageManagerException {
@@ -773,12 +770,12 @@
                     "Updating application package " + pkgName + " failed");
         }
 
-        if (!pkgSetting.codePath.equals(codePath)) {
+        if (!pkgSetting.getCodePath().equals(codePath)) {
             final boolean isSystem = pkgSetting.isSystem();
             Slog.i(PackageManagerService.TAG,
                     "Update" + (isSystem ? " system" : "")
                     + " package " + pkgName
-                    + " code path from " + pkgSetting.codePathString
+                    + " code path from " + pkgSetting.getCodePathString()
                     + " to " + codePath.toString()
                     + "; Retain data and using new");
             if (!isSystem) {
@@ -800,19 +797,7 @@
                 // internal to external storage or vice versa.
                 pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
             }
-            pkgSetting.codePath = codePath;
-            pkgSetting.codePathString = codePath.toString();
-        }
-        if (!pkgSetting.resourcePath.equals(resourcePath)) {
-            final boolean isSystem = pkgSetting.isSystem();
-            Slog.i(PackageManagerService.TAG,
-                    "Update" + (isSystem ? " system" : "")
-                    + " package " + pkgName
-                    + " resource path from " + pkgSetting.resourcePathString
-                    + " to " + resourcePath.toString()
-                    + "; Retain data and using new");
-            pkgSetting.resourcePath = resourcePath;
-            pkgSetting.resourcePathString = resourcePath.toString();
+            pkgSetting.setCodePath(codePath);
         }
         // If what we are scanning is a system (and possibly privileged) package,
         // then make it so, regardless of whether it was previously installed only
@@ -2710,7 +2695,7 @@
 
     private void writePackageListLPrInternal(int creatingUserId) {
         // Only derive GIDs for active users (not dying)
-        final List<UserInfo> users = getUsers(UserManagerService.getInstance(), true);
+        final List<UserInfo> users = getActiveUsers(UserManagerService.getInstance(), true);
         int[] userIds = new int[users.size()];
         for (int i = 0; i < userIds.length; i++) {
             userIds[i] = users.get(i).id;
@@ -2812,14 +2797,11 @@
         if (pkg.realName != null) {
             serializer.attribute(null, "realName", pkg.realName);
         }
-        serializer.attribute(null, "codePath", pkg.codePathString);
+        serializer.attribute(null, "codePath", pkg.getCodePathString());
         serializer.attribute(null, "ft", Long.toHexString(pkg.timeStamp));
         serializer.attribute(null, "it", Long.toHexString(pkg.firstInstallTime));
         serializer.attribute(null, "ut", Long.toHexString(pkg.lastUpdateTime));
         serializer.attribute(null, "version", String.valueOf(pkg.versionCode));
-        if (!pkg.resourcePathString.equals(pkg.codePathString)) {
-            serializer.attribute(null, "resourcePath", pkg.resourcePathString);
-        }
         if (pkg.legacyNativeLibraryPathString != null) {
             serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
         }
@@ -2857,10 +2839,7 @@
         if (pkg.realName != null) {
             serializer.attribute(null, "realName", pkg.realName);
         }
-        serializer.attribute(null, "codePath", pkg.codePathString);
-        if (!pkg.resourcePathString.equals(pkg.codePathString)) {
-            serializer.attribute(null, "resourcePath", pkg.resourcePathString);
-        }
+        serializer.attribute(null, "codePath", pkg.getCodePathString());
 
         if (pkg.legacyNativeLibraryPathString != null) {
             serializer.attribute(null, "nativeLibraryPath", pkg.legacyNativeLibraryPathString);
@@ -3559,13 +3538,10 @@
         String name = parser.getAttributeValue(null, ATTR_NAME);
         String realName = parser.getAttributeValue(null, "realName");
         String codePathStr = parser.getAttributeValue(null, "codePath");
-        String resourcePathStr = parser.getAttributeValue(null, "resourcePath");
 
         String legacyCpuAbiStr = parser.getAttributeValue(null, "requiredCpuAbi");
         String legacyNativeLibraryPathStr = parser.getAttributeValue(null, "nativeLibraryPath");
 
-        String parentPackageName = parser.getAttributeValue(null, "parentPackageName");
-
         String primaryCpuAbiStr = parser.getAttributeValue(null, "primaryCpuAbi");
         String secondaryCpuAbiStr = parser.getAttributeValue(null, "secondaryCpuAbi");
         String cpuAbiOverrideStr = parser.getAttributeValue(null, "cpuAbiOverride");
@@ -3574,9 +3550,6 @@
             primaryCpuAbiStr = legacyCpuAbiStr;
         }
 
-        if (resourcePathStr == null) {
-            resourcePathStr = codePathStr;
-        }
         String version = parser.getAttributeValue(null, "version");
         long versionCode = 0;
         if (version != null) {
@@ -3593,9 +3566,8 @@
             pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         }
         PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
-                new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiStr,
-                secondaryCpuAbiStr, cpuAbiOverrideStr, versionCode, pkgFlags, pkgPrivateFlags,
-                0 /*sharedUserId*/, null, null, null);
+                legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
+                versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null);
         String timeStampStr = parser.getAttributeValue(null, "ft");
         if (timeStampStr != null) {
             try {
@@ -3666,7 +3638,6 @@
         String idStr = null;
         String sharedIdStr = null;
         String codePathStr = null;
-        String resourcePathStr = null;
         String legacyCpuAbiString = null;
         String legacyNativeLibraryPathStr = null;
         String primaryCpuAbiString = null;
@@ -3700,7 +3671,6 @@
             uidError = parser.getAttributeValue(null, "uidError");
             sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
             codePathStr = parser.getAttributeValue(null, "codePath");
-            resourcePathStr = parser.getAttributeValue(null, "resourcePath");
 
             legacyCpuAbiString = parser.getAttributeValue(null, "requiredCpuAbi");
 
@@ -3818,9 +3788,6 @@
                         + " sharedUserId=" + sharedIdStr);
             final int userId = idStr != null ? Integer.parseInt(idStr) : 0;
             final int sharedUserId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
-            if (resourcePathStr == null) {
-                resourcePathStr = codePathStr;
-            }
             if (realName != null) {
                 realName = realName.intern();
             }
@@ -3834,10 +3801,10 @@
                                 + parser.getPositionDescription());
             } else if (userId > 0) {
                 packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
-                        new File(resourcePathStr), legacyNativeLibraryPathStr, primaryCpuAbiString,
-                        secondaryCpuAbiString, cpuAbiOverrideString, userId, versionCode, pkgFlags,
-                        pkgPrivateFlags, null /*usesStaticLibraries*/,
-                        null /*usesStaticLibraryVersions*/, null /*mimeGroups*/);
+                        legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
+                        cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
+                        null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
+                        null /*mimeGroups*/);
                 if (PackageManagerService.DEBUG_SETTINGS)
                     Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
                             + userId + " pkg=" + packageSetting);
@@ -3852,8 +3819,8 @@
                 }
             } else if (sharedIdStr != null) {
                 if (sharedUserId > 0) {
-                    packageSetting = new PackageSetting(name.intern(), realName, new File(
-                            codePathStr), new File(resourcePathStr), legacyNativeLibraryPathStr,
+                    packageSetting = new PackageSetting(name.intern(), realName,
+                            new File(codePathStr), legacyNativeLibraryPathStr,
                             primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
                             versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
                             null /*usesStaticLibraries*/,
@@ -4456,25 +4423,43 @@
     }
 
     /**
-     * Return all users on the device, including partial or dying users.
+     * Returns all users on the device, including pre-created and dying users.
+     *
      * @param userManager UserManagerService instance
      * @return the list of users
      */
     private static List<UserInfo> getAllUsers(UserManagerService userManager) {
-        return getUsers(userManager, false);
+        return getUsers(userManager, /* excludeDying= */ false, /* excludePreCreated= */ false);
     }
 
     /**
-     * Return the list of users on the device. Clear the calling identity before calling into
-     * UserManagerService.
+     * Returns the list of users on the device, excluding pre-created ones.
+     *
      * @param userManager UserManagerService instance
      * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     *
      * @return the list of users
      */
-    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying) {
+    private static List<UserInfo> getActiveUsers(UserManagerService userManager,
+            boolean excludeDying) {
+        return getUsers(userManager, excludeDying, /* excludePreCreated= */ true);
+    }
+
+    /**
+     * Returns the list of users on the device.
+     *
+     * @param userManager UserManagerService instance
+     * @param excludeDying Indicates whether to exclude any users marked for deletion.
+     * @param excludePreCreated Indicates whether to exclude any pre-created users.
+     *
+     * @return the list of users
+     */
+    private static List<UserInfo> getUsers(UserManagerService userManager, boolean excludeDying,
+            boolean excludePreCreated) {
         long id = Binder.clearCallingIdentity();
         try {
-            return userManager.getUsers(excludeDying);
+            return userManager.getUsers(/* excludePartial= */ true, excludeDying,
+                    excludePreCreated);
         } catch (NullPointerException npe) {
             // packagemanager not yet initialized
         } finally {
@@ -4657,9 +4642,9 @@
             pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.sharedUser);
         }
         pw.print(prefix); pw.print("  pkg="); pw.println(pkg);
-        pw.print(prefix); pw.print("  codePath="); pw.println(ps.codePathString);
+        pw.print(prefix); pw.print("  codePath="); pw.println(ps.getCodePathString());
         if (permissionNames == null) {
-            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.resourcePathString);
+            pw.print(prefix); pw.print("  resourcePath="); pw.println(ps.getCodePathString());
             pw.print(prefix); pw.print("  legacyNativeLibraryDir=");
             pw.println(ps.legacyNativeLibraryPathString);
             pw.print(prefix); pw.print("  primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 89bdb3e..f9bf54a 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -322,9 +322,6 @@
         }
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         if (activeVersion != session.params.requiredInstalledVersionCode) {
-            if (!mApexManager.abortStagedSession(session.sessionId)) {
-                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
-            }
             throw new PackageManagerException(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Installed version of APEX package " + activePackage.packageName
@@ -338,14 +335,11 @@
             throws PackageManagerException {
         final long activeVersion = activePackage.applicationInfo.longVersionCode;
         final long newVersionCode = newPackage.applicationInfo.longVersionCode;
-        boolean isAppDebuggable = (activePackage.applicationInfo.flags
+        final boolean isAppDebuggable = (activePackage.applicationInfo.flags
                 & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
         final boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted(
                 session.params.installFlags, isAppDebuggable);
         if (activeVersion > newVersionCode && !allowsDowngrade) {
-            if (!mApexManager.abortStagedSession(session.sessionId)) {
-                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
-            }
             throw new PackageManagerException(
                     SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
                     "Downgrade of APEX package " + newPackage.packageName
@@ -835,37 +829,6 @@
         return null;
     }
 
-    private void verifyApksInSession(PackageInstallerSession session)
-            throws PackageManagerException {
-
-        final PackageInstallerSession apksToVerify = extractApksInSession(
-                session,  /* preReboot */ true);
-        if (apksToVerify == null) {
-            return;
-        }
-
-        final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
-                (Intent result) -> {
-                    int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                            PackageInstaller.STATUS_FAILURE);
-                    if (status != PackageInstaller.STATUS_SUCCESS) {
-                        final String errorMessage = result.getStringExtra(
-                                PackageInstaller.EXTRA_STATUS_MESSAGE);
-                        Slog.e(TAG, "Failure to verify APK staged session "
-                                + session.sessionId + " [" + errorMessage + "]");
-                        session.setStagedSessionFailed(
-                                SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
-                        mPreRebootVerificationHandler.onPreRebootVerificationComplete(
-                                session.sessionId);
-                        return;
-                    }
-                    mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
-                            session.sessionId);
-                });
-
-        apksToVerify.commit(receiver.getIntentSender(), false);
-    }
-
     private void installApksInSession(@NonNull PackageInstallerSession session)
             throws PackageManagerException {
 
@@ -908,10 +871,21 @@
         mPreRebootVerificationHandler.startPreRebootVerification(session.sessionId);
     }
 
-    private int parentOrOwnSessionId(PackageInstallerSession session) {
+    private int getSessionIdForParentOrSelf(PackageInstallerSession session) {
         return session.hasParentSessionId() ? session.getParentSessionId() : session.sessionId;
     }
 
+    private PackageInstallerSession getParentSessionOrSelf(PackageInstallerSession session) {
+        return session.hasParentSessionId()
+                ? getStagedSession(session.getParentSessionId())
+                : session;
+    }
+
+    private boolean isRollback(PackageInstallerSession session) {
+        final PackageInstallerSession root = getParentSessionOrSelf(session);
+        return root.params.installReason == PackageManager.INSTALL_REASON_ROLLBACK;
+    }
+
     /**
      * <p> Check if the session provided is non-overlapping with the active staged sessions.
      *
@@ -937,6 +911,8 @@
         boolean supportsCheckpoint = ((StorageManager) mContext.getSystemService(
                 Context.STORAGE_SERVICE)).isCheckpointSupported();
 
+        final boolean isRollback = isRollback(session);
+
         synchronized (mStagedSessions) {
             for (int i = 0; i < mStagedSessions.size(); i++) {
                 final PackageInstallerSession stagedSession = mStagedSessions.valueAt(i);
@@ -951,8 +927,8 @@
                 }
                 // Check if stagedSession has an active parent session or not
                 if (stagedSession.hasParentSessionId()) {
-                    int parentId = stagedSession.getParentSessionId();
-                    PackageInstallerSession parentSession = mStagedSessions.get(parentId);
+                    final int parentId = stagedSession.getParentSessionId();
+                    final PackageInstallerSession parentSession = mStagedSessions.get(parentId);
                     if (parentSession == null || parentSession.isStagedAndInTerminalState()
                             || parentSession.isDestroyed()) {
                         // Parent session has been abandoned or terminated already
@@ -968,21 +944,37 @@
                     continue;
                 }
 
-                // If session is not among the active sessions, then it cannot have same package
-                // name as any of the active sessions.
+                // New session cannot have same package name as one of the active sessions
                 if (session.getPackageName().equals(stagedSession.getPackageName())) {
-                    throw new PackageManagerException(
-                            PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
-                            "Package: " + session.getPackageName() + " in session: "
-                                    + session.sessionId + " has been staged already by session: "
-                                    + stagedSession.sessionId, null);
+                    if (isRollback) {
+                        // If the new session is a rollback, then it gets priority. The existing
+                        // session is failed to unblock rollback.
+                        final PackageInstallerSession root = getParentSessionOrSelf(stagedSession);
+                        if (!ensureActiveApexSessionIsAborted(root)) {
+                            Slog.e(TAG, "Failed to abort apex session " + root.sessionId);
+                            // Safe to ignore active apex session abort failure since session
+                            // will be marked failed on next step and staging directory for session
+                            // will be deleted.
+                        }
+                        root.setStagedSessionFailed(
+                                SessionInfo.STAGED_SESSION_OTHER_ERROR,
+                                "Session was blocking rollback session: " + session.sessionId);
+                        Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
+                                + "blocking rollback session: " + session.sessionId);
+                    } else {
+                        throw new PackageManagerException(
+                                PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
+                                "Package: " + session.getPackageName() + " in session: "
+                                        + session.sessionId + " has been staged already by session:"
+                                        + " " + stagedSession.sessionId, null);
+                    }
                 }
 
                 // Staging multiple root sessions is not allowed if device doesn't support
                 // checkpoint. If session and stagedSession do not have common ancestor, they are
                 // from two different root sessions.
-                if (!supportsCheckpoint
-                        && parentOrOwnSessionId(session) != parentOrOwnSessionId(stagedSession)) {
+                if (!supportsCheckpoint && getSessionIdForParentOrSelf(session)
+                        != getSessionIdForParentOrSelf(stagedSession)) {
                     throw new PackageManagerException(
                             PackageManager.INSTALL_FAILED_OTHER_STAGED_SESSION_IN_PROGRESS,
                             "Cannot stage multiple sessions without checkpoint support", null);
@@ -1042,23 +1034,11 @@
 
         // A session could be marked ready once its pre-reboot verification ends
         if (session.isStagedSessionReady()) {
-            if (sessionContainsApex(session)) {
-                try {
-                    ApexSessionInfo apexSession =
-                            mApexManager.getStagedSessionInfo(session.sessionId);
-                    if (apexSession == null || isApexSessionFinalized(apexSession)) {
-                        Slog.w(TAG,
-                                "Cannot abort session " + session.sessionId
-                                        + " because it is not active.");
-                    } else {
-                        mApexManager.abortStagedSession(session.sessionId);
-                    }
-                } catch (Exception e) {
-                    // Failed to contact apexd service. The apex might still be staged. We can still
-                    // safely cleanup the staged session since pre-reboot verification is complete.
-                    // Also, cleaning up the stageDir prevents the apex from being activated.
-                    Slog.w(TAG, "Could not contact apexd to abort staged session " + sessionId);
-                }
+            if (!ensureActiveApexSessionIsAborted(session)) {
+                // Failed to ensure apex session is aborted, so it can still be staged. We can still
+                // safely cleanup the staged session since pre-reboot verification is complete.
+                // Also, cleaning up the stageDir prevents the apex from being activated.
+                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
             }
         }
 
@@ -1068,6 +1048,22 @@
         return true;
     }
 
+    /**
+     * Ensure that there is no active apex session staged in apexd for the given session.
+     *
+     * @return returns true if it is ensured that there is no active apex session, otherwise false
+     */
+    private boolean ensureActiveApexSessionIsAborted(PackageInstallerSession session) {
+        if (!sessionContainsApex(session)) {
+            return true;
+        }
+        final ApexSessionInfo apexSession = mApexManager.getStagedSessionInfo(session.sessionId);
+        if (apexSession == null || isApexSessionFinalized(apexSession)) {
+            return true;
+        }
+        return mApexManager.abortStagedSession(session.sessionId);
+    }
+
     private boolean isApexSessionFinalized(ApexSessionInfo session) {
         /* checking if the session is in a final state, i.e., not active anymore */
         return session.isUnknown || session.isActivationFailed || session.isSuccess
@@ -1294,8 +1290,8 @@
                         + sessionId);
                 return;
             }
-            if (session.isDestroyed()) {
-                // No point in running verification on a destroyed session
+            if (session.isDestroyed() || session.isStagedSessionFailed()) {
+                // No point in running verification on a destroyed/failed session
                 onPreRebootVerificationComplete(sessionId);
                 return;
             }
@@ -1348,6 +1344,17 @@
             obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, sessionId, 0).sendToTarget();
         }
 
+        private void onPreRebootVerificationFailure(PackageInstallerSession session,
+                @SessionInfo.StagedSessionErrorCode int errorCode, String errorMessage) {
+            if (!ensureActiveApexSessionIsAborted(session)) {
+                Slog.e(TAG, "Failed to abort apex session " + session.sessionId);
+                // Safe to ignore active apex session abortion failure since session will be marked
+                // failed on next step and staging directory for session will be deleted.
+            }
+            session.setStagedSessionFailed(errorCode, errorMessage);
+            onPreRebootVerificationComplete(session.sessionId);
+        }
+
         // Things to do when pre-reboot verification completes for a particular sessionId
         private void onPreRebootVerificationComplete(int sessionId) {
             // Remove it from mVerificationRunning so that verification is considered complete
@@ -1432,8 +1439,7 @@
                         validateApexSignature(apexPackages.get(i));
                     }
                 } catch (PackageManagerException e) {
-                    session.setStagedSessionFailed(e.error, e.getMessage());
-                    onPreRebootVerificationComplete(session.sessionId);
+                    onPreRebootVerificationFailure(session, e.error, e.getMessage());
                     return;
                 }
 
@@ -1460,16 +1466,42 @@
             try {
                 Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
                         + session.sessionId + " by performing a dry-run install");
-
                 // verifyApksInSession will notify the handler when APK verification is complete
                 verifyApksInSession(session);
-                // TODO(b/118865310): abort the session on apexd.
             } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                onPreRebootVerificationComplete(session.sessionId);
+                onPreRebootVerificationFailure(session, e.error, e.getMessage());
             }
         }
 
+        private void verifyApksInSession(PackageInstallerSession session)
+                throws PackageManagerException {
+
+            final PackageInstallerSession apksToVerify = extractApksInSession(
+                    session,  /* preReboot */ true);
+            if (apksToVerify == null) {
+                return;
+            }
+
+            final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+                    (Intent result) -> {
+                        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                                PackageInstaller.STATUS_FAILURE);
+                        if (status != PackageInstaller.STATUS_SUCCESS) {
+                            final String errorMessage = result.getStringExtra(
+                                    PackageInstaller.EXTRA_STATUS_MESSAGE);
+                            Slog.e(TAG, "Failure to verify APK staged session "
+                                    + session.sessionId + " [" + errorMessage + "]");
+                            onPreRebootVerificationFailure(session,
+                                    SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+                            return;
+                        }
+                        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+                                session.sessionId);
+                    });
+
+            apksToVerify.commit(receiver.getIntentSender(), false);
+        }
+
         /**
          * Pre-reboot verification state for wrapping up:
          * <p><ul>
@@ -1487,9 +1519,8 @@
             } catch (Exception e) {
                 // Failed to get hold of StorageManager
                 Slog.e(TAG, "Failed to get hold of StorageManager", e);
-                session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_UNKNOWN,
+                onPreRebootVerificationFailure(session, SessionInfo.STAGED_SESSION_UNKNOWN,
                         "Failed to get hold of StorageManager");
-                onPreRebootVerificationComplete(session.sessionId);
                 return;
             }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8f11fd5..e3bee72 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3497,7 +3497,7 @@
             }
 
             t.traceBegin("PM.onNewUserCreated-" + userId);
-            mPm.onNewUserCreated(userId);
+            mPm.onNewUserCreated(userId, /* convertedFromPreCreated= */ false);
             t.traceEnd();
             if (preCreate) {
                 // Must start user (which will be stopped right away, through
@@ -3570,10 +3570,7 @@
             writeUserListLP();
         }
         updateUserIds();
-        if (!mPm.readPermissionStateForUser(preCreatedUser.id)) {
-            // Could not read the existing permissions, re-grant them.
-            mPm.onNewUserCreated(preCreatedUser.id);
-        }
+        mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
         dispatchUserAdded(preCreatedUser);
         return preCreatedUser;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a05289f..2c475e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -130,6 +130,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.view.Display;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -1090,9 +1091,9 @@
         // Check if caller is already present on display
         final boolean uidPresentOnDisplay = displayContent.isUidPresent(callingUid);
 
-        final int displayOwnerUid = displayContent.mDisplay.getOwnerUid();
-        if (displayContent.mDisplay.getType() == TYPE_VIRTUAL && displayOwnerUid != SYSTEM_UID) {
-            // Limit launching on virtual displays, because their contents can be read from Surface
+        final Display display = displayContent.mDisplay;
+        if (!display.isTrusted()) {
+            // Limit launching on untrusted displays because their contents can be read from Surface
             // by apps that created them.
             if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
                 if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -1116,7 +1117,7 @@
         }
 
         // Check if the caller is the owner of the display.
-        if (displayOwnerUid == callingUid) {
+        if (display.getOwnerUid() == callingUid) {
             if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
                     + " allow launch for owner of the display");
             return true;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index f47ddef..be0815b0 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2365,7 +2365,6 @@
     private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
         if (mWmService.mDisableTransitionAnimation
                 || !isVisible()
-                || getDisplayContent().mAppTransition.isTransitionSet()
                 || getSurfaceControl() == null
                 || !isLeafTask()) {
             return false;
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index f3c7a5d..9b18ac8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.graphics.Color.WHITE;
 import static android.graphics.Color.alpha;
 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
@@ -142,6 +143,7 @@
     private final Handler mHandler;
     private boolean mSizeMismatch;
     private final Paint mBackgroundPaint = new Paint();
+    private final int mActivityType;
     private final int mStatusBarColor;
     @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter;
     private final int mOrientationOnCreation;
@@ -173,6 +175,7 @@
         final int windowFlags;
         final int windowPrivateFlags;
         final int currentOrientation;
+        final int activityType;
         final InsetsState insetsState;
         synchronized (service.mGlobalLock) {
             final WindowState mainWindow = activity.findMainWindow();
@@ -241,6 +244,7 @@
             taskBounds = new Rect();
             task.getBounds(taskBounds);
             currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation;
+            activityType = activity.getActivityType();
 
             final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
                     .getInsetsPolicy();
@@ -261,7 +265,8 @@
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
                 surfaceControl, snapshot, layoutParams.getTitle(), taskDescription, sysUiVis,
-                windowFlags, windowPrivateFlags, taskBounds, currentOrientation, insetsState);
+                windowFlags, windowPrivateFlags, taskBounds, currentOrientation, activityType,
+                insetsState);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
@@ -282,7 +287,7 @@
     TaskSnapshotSurface(WindowManagerService service, Window window, SurfaceControl surfaceControl,
             TaskSnapshot snapshot, CharSequence title, TaskDescription taskDescription,
             int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
-            int currentOrientation, InsetsState insetsState) {
+            int currentOrientation, int activityType, InsetsState insetsState) {
         mService = service;
         mSurface = service.mSurfaceFactory.get();
         mHandler = new Handler(mService.mH.getLooper());
@@ -298,6 +303,7 @@
                 windowPrivateFlags, sysUiVis, taskDescription, 1f, insetsState);
         mStatusBarColor = taskDescription.getStatusBarColor();
         mOrientationOnCreation = currentOrientation;
+        mActivityType = activityType;
         mTransaction = mService.mTransactionFactory.get();
     }
 
@@ -305,7 +311,9 @@
     public void remove() {
         synchronized (mService.mGlobalLock) {
             final long now = SystemClock.uptimeMillis();
-            if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS) {
+            if (mSizeMismatch && now - mShownTime < SIZE_MISMATCH_MINIMUM_TIME_MS
+                    // Show the latest content as soon as possible for unlocking to home.
+                    && mActivityType != ACTIVITY_TYPE_HOME) {
                 mHandler.postAtTime(this::remove, mShownTime + SIZE_MISMATCH_MINIMUM_TIME_MS);
                 ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
                         "Defer removing snapshot surface in %dms", (now - mShownTime));
diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index ecdb30f..09552082 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -33,7 +33,6 @@
 import com.android.server.pm.parsing.pkg.ParsedPackage
 import com.android.server.pm.permission.PermissionManagerServiceInternal
 import com.android.server.pm.test.override.PackageManagerComponentLabelIconOverrideTest.Companion.Params.AppType
-import com.android.server.pm.test.override.R
 import com.android.server.testutils.TestHandler
 import com.android.server.testutils.mock
 import com.android.server.testutils.mockThrowOnUnmocked
@@ -266,7 +265,7 @@
                     .hideAsFinal()
 
     private fun makePkgSetting(pkgName: String) = spy(PackageSetting(pkgName, null, File("/test"),
-            File("/test"), null, null, null, null, 0, 0, 0, 0, null, null, null)) {
+            null, null, null, null, 0, 0, 0, 0, null, null, null)) {
         this.pkgState.isUpdatedSystemApp = params.isUpdatedSystemApp
     }
 
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 41dfade..cffcdd8 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -25,17 +25,28 @@
     ],
     test_suites: ["general-tests"],
     java_resources: [
-        ":PackageManagerDummyAppVersion1",
-        ":PackageManagerDummyAppVersion2",
-        ":PackageManagerDummyAppVersion3",
-        ":PackageManagerDummyAppVersion4",
-        ":PackageManagerDummyAppOriginalOverride",
-        ":PackageManagerServiceHostTestsResources",
-    ]
+        ":PackageManagerTestAppStub",
+        ":PackageManagerTestAppVersion1",
+        ":PackageManagerTestAppVersion2",
+        ":PackageManagerTestAppVersion3",
+        ":PackageManagerTestAppVersion3Invalid",
+        ":PackageManagerTestAppVersion4",
+        ":PackageManagerTestAppOriginalOverride",
+    ],
 }
 
-filegroup {
-    name: "PackageManagerServiceHostTestsResources",
-    srcs: [ "resources/*" ],
-    path: "resources/"
+genrule {
+    name: "PackageManagerTestAppVersion3Invalid",
+    tools: [
+        "soong_zip",
+        "zipalign",
+    ],
+    srcs: [
+        ":PackageManagerTestAppVersion3",
+    ],
+    out: ["PackageManagerTestAppVersion3Invalid.apk"],
+    cmd: "mkdir -p $(genDir)/apk && unzip $(in) -d $(genDir)/apk" +
+        " && truncate -s 800 $(genDir)/apk/META-INF/CERT.RSA" +
+        " && $(location soong_zip) -o $(genDir)/temp.apk -L 0 -C $(genDir)/apk -D $(genDir)/apk" +
+        " && $(location zipalign) -f 4 $(genDir)/temp.apk $(out)",
 }
diff --git a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk b/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
deleted file mode 100644
index 127886c..0000000
--- a/services/tests/PackageManagerServiceTests/host/resources/PackageManagerDummyAppVersion3Invalid.apk
+++ /dev/null
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 234fcf1..8dfefaf 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -21,8 +21,9 @@
 import java.io.File
 import java.io.FileOutputStream
 
-internal fun SystemPreparer.pushApk(file: String, partition: Partition) =
-        pushResourceFile(file, HostUtils.makePathForApk(file, partition).toString())
+internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
+        pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
+                .toString())
 
 internal fun SystemPreparer.deleteApkFolders(
     partition: Partition,
@@ -58,4 +59,55 @@
         }
         return file
     }
+
+    /**
+     * dumpsys package and therefore device.getAppPackageInfo doesn't work immediately after reboot,
+     * so the following methods parse the package dump directly to see if the path matches.
+     */
+    fun getCodePaths(device: ITestDevice, pkgName: String) =
+            device.executeShellCommand("pm dump $pkgName")
+                    .lineSequence()
+                    .map(String::trim)
+                    .filter { it.startsWith("codePath=") }
+                    .map { it.removePrefix("codePath=") }
+                    .toList()
+
+    private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
+            device.executeShellCommand("pm dump $pkgName")
+                    .lineSequence()
+                    .dropWhile { !it.startsWith("Packages:") }
+                    .takeWhile {
+                        !it.startsWith("Hidden system packages:") &&
+                                !it.startsWith("Queries:")
+                    }
+                    .map(String::trim)
+                    .filter { it.startsWith("User ") }
+
+    fun getUserIdToPkgEnabledState(device: ITestDevice, pkgName: String) =
+            userIdLineSequence(device, pkgName).associate {
+                val userId = it.removePrefix("User ")
+                        .takeWhile(Char::isDigit)
+                        .toInt()
+                val enabled = it.substringAfter("enabled=")
+                        .takeWhile(Char::isDigit)
+                        .toInt()
+                        .let {
+                            when (it) {
+                                0, 1 -> true
+                                else -> false
+                            }
+                        }
+                userId to enabled
+            }
+
+    fun getUserIdToPkgInstalledState(device: ITestDevice, pkgName: String) =
+            userIdLineSequence(device, pkgName).associate {
+                val userId = it.removePrefix("User ")
+                        .takeWhile(Char::isDigit)
+                        .toInt()
+                val installed = it.substringAfter("installed=")
+                        .takeWhile { !it.isWhitespace() }
+                        .toBoolean()
+                userId to installed
+            }
 }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index 39b40d8..b7d1359 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -33,11 +33,11 @@
 class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
-        private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
-        private const val VERSION_THREE_INVALID = "PackageManagerDummyAppVersion3Invalid.apk"
-        private const val VERSION_FOUR = "PackageManagerDummyAppVersion4.apk"
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+        private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
+        private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
 
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -49,14 +49,14 @@
 
     @get:Rule
     val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
-    private val filePath = HostUtils.makePathForApk("PackageManagerDummyApp.apk", Partition.PRODUCT)
+    private val filePath = HostUtils.makePathForApk("PackageManagerTestApp.apk", Partition.PRODUCT)
 
     @Before
     @After
     fun removeApk() {
         device.uninstallPackage(TEST_PKG_NAME)
-        device.deleteFile(filePath.parent.toString())
-        device.reboot()
+        preparer.deleteFile(filePath.parent.toString())
+                .reboot()
     }
 
     @Test
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index fb0348c..4ae3ca5 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -33,11 +33,11 @@
 class OriginalPackageMigrationTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.dummy_app"
-        private const val VERSION_ONE = "PackageManagerDummyAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerDummyAppVersion2.apk"
-        private const val VERSION_THREE = "PackageManagerDummyAppVersion3.apk"
-        private const val NEW_PKG = "PackageManagerDummyAppOriginalOverride.apk"
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+        private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
+        private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk"
 
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -55,6 +55,7 @@
     fun deleteApkFolders() {
         preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
                 NEW_PKG)
+                .reboot()
     }
 
     @Test
@@ -99,9 +100,7 @@
     }
 
     private fun assertCodePath(apk: String) {
-        // dumpsys package and therefore device.getAppPackageInfo doesn't work here for some reason,
-        // so parse the package dump directly to see if the path matches.
-        assertThat(device.executeShellCommand("pm dump $TEST_PKG_NAME"))
-                .contains(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
+        assertThat(HostUtils.getCodePaths(device, TEST_PKG_NAME))
+                .containsExactly(HostUtils.makePathForApk(apk, Partition.SYSTEM).parent.toString())
     }
 }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
new file mode 100644
index 0000000..bc478b0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -0,0 +1,650 @@
+/*
+ * 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.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.device.ITestDevice
+import com.android.tradefed.device.UserInfo
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.AfterClass
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+import java.util.zip.GZIPOutputStream
+
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
+
+    companion object {
+        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+        private const val VERSION_STUB = "PackageManagerTestAppStub.apk"
+        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+
+        /**
+         * How many total users on device to test, including primary. This will clean up any
+         * users created specifically for this test.
+         */
+        private const val USER_COUNT = 3
+
+        /**
+         * Whether to manually reset state at each test method without rebooting
+         * for faster iterative development.
+         */
+        private const val DEBUG_NO_REBOOT = false
+
+        @get:ClassRule
+        val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+
+        private val parentClassName = SystemStubMultiUserDisableUninstallTest::class.java.simpleName
+
+        private val deviceCompressedFile =
+                HostUtils.makePathForApk("$parentClassName.apk", Partition.PRODUCT).parent
+                        .resolve("$parentClassName.apk.gz")
+
+        private val stubFile =
+                HostUtils.makePathForApk("$parentClassName-Stub.apk", Partition.PRODUCT)
+
+        private val secondaryUsers = mutableListOf<Int>()
+        private val usersToRemove = mutableListOf<Int>()
+        private var savedDevice: ITestDevice? = null
+        private var savedPreparer: SystemPreparer? = null
+
+        private fun setUpUsers(device: ITestDevice) {
+            if (this.savedDevice != null) return
+            this.savedDevice = device
+            secondaryUsers.clear()
+            secondaryUsers += device.userInfos.values.map(UserInfo::userId).filterNot { it == 0 }
+            while (secondaryUsers.size < USER_COUNT) {
+                secondaryUsers += device.createUser(parentClassName + secondaryUsers.size)
+                        .also { usersToRemove += it }
+            }
+        }
+
+        @JvmStatic
+        @AfterClass
+        fun cleanUp() {
+            savedDevice ?: return
+
+            usersToRemove.forEach {
+                savedDevice?.removeUser(it)
+            }
+
+            savedDevice?.uninstallPackage(TEST_PKG_NAME)
+            savedDevice?.deleteFile(stubFile.parent.toString())
+            savedDevice?.deleteFile(deviceCompressedFile.parent.toString())
+            savedDevice?.reboot()
+            savedDevice = null
+
+            if (DEBUG_NO_REBOOT) {
+                savedPreparer?.after()
+                savedPreparer = null
+            }
+        }
+    }
+
+    private val tempFolder = TemporaryFolder()
+    private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+            SystemPreparer.RebootStrategy.START_STOP, deviceRebootRule) { this.device }
+
+    @get:Rule
+    val rules = RuleChain.outerRule(tempFolder).let {
+        if (DEBUG_NO_REBOOT) {
+            it!!
+        } else {
+            it.around(preparer)!!
+        }
+    }
+
+    private var hostCompressedFile: File? = null
+
+    private val previousCodePaths = mutableListOf<String>()
+
+    @Before
+    fun ensureUserAndCompressStubAndInstall() {
+        setUpUsers(device)
+
+        val initialized = hostCompressedFile != null
+        if (!initialized) {
+            hostCompressedFile = tempFolder.newFile()
+            hostCompressedFile!!.outputStream().use {
+                javaClass.classLoader
+                        .getResource(VERSION_ONE)!!
+                        .openStream()
+                        .use { input ->
+                            GZIPOutputStream(it).use { output ->
+                                input.copyTo(output)
+                            }
+                        }
+            }
+        }
+
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        if (!initialized || !DEBUG_NO_REBOOT) {
+            savedPreparer = preparer
+            preparer.pushResourceFile(VERSION_STUB, stubFile.toString())
+                    .pushFile(hostCompressedFile, deviceCompressedFile.toString())
+                    .reboot()
+        }
+
+        // This test forces the state to installed/enabled for all users,
+        // since it only tests the uninstall/disable side.
+        installExisting(User.PRIMARY)
+        installExisting(User.SECONDARY)
+
+        ensureEnabled()
+
+        // Ensure data app isn't re-installed multiple times by comparing against the original path
+        val codePath = HostUtils.getCodePaths(device, TEST_PKG_NAME).first()
+        assertThat(codePath).contains("/data/app")
+        assertThat(codePath).contains(TEST_PKG_NAME)
+
+        previousCodePaths.clear()
+        previousCodePaths += codePath
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disablePrimaryFirstAndUninstall() {
+        toggleEnabled(false, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(false, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disableSecondaryFirstAndUninstall() {
+        toggleEnabled(false, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(false, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstalledEnablePrimaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstalledEnableSecondaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+        device.uninstallPackage(TEST_PKG_NAME)
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallPrimaryFirstByUserAndInstallExistingPrimaryFirst() {
+        uninstall(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        uninstall(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        installExisting(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        installExisting(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallSecondaryFirstByUserAndInstallExistingSecondaryFirst() {
+        uninstall(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        uninstall(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        installExisting(User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        installExisting(User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallUpdatesAndEnablePrimaryFirst() {
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        // TODO: Is this intentional? There is no user argument for this command.
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                // If any user is enabled when uninstalling updates, /data is re-uncompressed
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        // Test enabling secondary to ensure path does not change, even though it's already enabled
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstallUpdatesAndEnableSecondaryFirst() {
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                // If any user is enabled when uninstalling updates, /data is re-uncompressed
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstallUpdatesAndEnablePrimaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun disabledUninstallUpdatesAndEnableSecondaryFirst() {
+        toggleEnabled(false, User.PRIMARY)
+        toggleEnabled(false, User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = false,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = false,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = true, primaryEnabled = true,
+                secondaryInstalled = true, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstalledUninstallUpdatesAndEnablePrimaryFirst() {
+        uninstall(User.PRIMARY)
+        uninstall(User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        assertState(
+                primaryInstalled = false, primaryEnabled = false,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    @Test
+    fun uninstalledUninstallUpdatesAndEnableSecondaryFirst() {
+        uninstall(User.PRIMARY)
+        uninstall(User.SECONDARY)
+
+        device.executeShellCommand("pm uninstall-system-updates $TEST_PKG_NAME")
+
+        // Uninstall-system-updates always disables system user 0
+        assertState(
+                primaryInstalled = false, primaryEnabled = false,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.SECONDARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = false,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.DIFFERENT, CodePath.SYSTEM)
+        )
+
+        toggleEnabled(true, User.PRIMARY)
+
+        assertState(
+                primaryInstalled = false, primaryEnabled = true,
+                secondaryInstalled = false, secondaryEnabled = true,
+                codePaths = listOf(CodePath.SAME, CodePath.SYSTEM)
+        )
+    }
+
+    private fun ensureEnabled() {
+        toggleEnabled(true, User.PRIMARY)
+        toggleEnabled(true, User.SECONDARY)
+
+        assertThat(HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME).all { it.value })
+                .isTrue()
+    }
+
+    private fun toggleEnabled(enabled: Boolean, user: User, pkgName: String = TEST_PKG_NAME) {
+        val command = if (enabled) "enable" else "disable"
+        @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+            User.PRIMARY -> {
+                device.executeShellCommand("pm $command --user 0 $pkgName")
+            }
+            User.SECONDARY -> {
+                secondaryUsers.forEach {
+                    device.executeShellCommand("pm $command --user $it $pkgName")
+                }
+            }
+        }
+    }
+
+    private fun uninstall(user: User, pkgName: String = TEST_PKG_NAME) {
+        @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+            User.PRIMARY -> {
+                device.executeShellCommand("pm uninstall --user 0 $pkgName")
+            }
+            User.SECONDARY -> {
+                secondaryUsers.forEach {
+                    device.executeShellCommand("pm uninstall --user $it $pkgName")
+                }
+            }
+        }
+    }
+
+    private fun installExisting(user: User, pkgName: String = TEST_PKG_NAME) {
+        @Suppress("UNUSED_VARIABLE") val exhaust: Any = when (user) {
+            User.PRIMARY -> {
+                device.executeShellCommand("pm install-existing --user 0 $pkgName")
+            }
+            User.SECONDARY -> {
+                secondaryUsers.forEach {
+                    device.executeShellCommand("pm install-existing --user $it $pkgName")
+                }
+            }
+        }
+    }
+
+    private fun assertState(
+        primaryInstalled: Boolean,
+        primaryEnabled: Boolean,
+        secondaryInstalled: Boolean,
+        secondaryEnabled: Boolean,
+        codePaths: List<CodePath>
+    ) {
+        HostUtils.getUserIdToPkgInstalledState(device, TEST_PKG_NAME)
+                .forEach { (userId, installed) ->
+                    if (userId == 0) {
+                        assertThat(installed).isEqualTo(primaryInstalled)
+                    } else {
+                        assertThat(installed).isEqualTo(secondaryInstalled)
+                    }
+                }
+
+        HostUtils.getUserIdToPkgEnabledState(device, TEST_PKG_NAME)
+                .forEach { (userId, enabled) ->
+                    if (userId == 0) {
+                        assertThat(enabled).isEqualTo(primaryEnabled)
+                    } else {
+                        assertThat(enabled).isEqualTo(secondaryEnabled)
+                    }
+                }
+
+        assertCodePaths(codePaths.first(), codePaths.getOrNull(1))
+    }
+
+    private fun assertCodePaths(firstCodePath: CodePath, secondCodePath: CodePath? = null) {
+        val codePaths = HostUtils.getCodePaths(device, TEST_PKG_NAME)
+        assertThat(codePaths).hasSize(listOfNotNull(firstCodePath, secondCodePath).size)
+
+        when (firstCodePath) {
+            CodePath.SAME -> {
+                assertThat(codePaths[0]).contains("/data/app")
+                assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+                assertThat(codePaths[0]).isEqualTo(previousCodePaths.last())
+            }
+            CodePath.DIFFERENT -> {
+                assertThat(codePaths[0]).contains("/data/app")
+                assertThat(codePaths[0]).contains(TEST_PKG_NAME)
+                assertThat(previousCodePaths).doesNotContain(codePaths[0])
+                previousCodePaths.add(codePaths[0])
+            }
+            CodePath.SYSTEM -> assertThat(codePaths[0]).isEqualTo(stubFile.parent.toString())
+        }
+
+        when (secondCodePath) {
+            CodePath.SAME, CodePath.DIFFERENT ->
+                throw AssertionError("secondDataPath cannot be a data path")
+            CodePath.SYSTEM -> assertThat(codePaths[1]).isEqualTo(stubFile.parent.toString())
+        }
+    }
+
+    enum class User {
+        /** The primary system user 0 */
+        PRIMARY,
+
+        /**
+         * All other users on the device that are not 0. This is split into an enum so that all
+         * methods that handle secondary act on all non-system users. Some behaviors only occur
+         * if a package state is marked for all non-primary users on the device, which can be
+         * more than just 1.
+         */
+        SECONDARY
+    }
+
+    enum class CodePath {
+        /** The data code path hasn't changed */
+        SAME,
+
+        /** New data code path */
+        DIFFERENT,
+
+        /** The static system code path */
+        SYSTEM
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
index c9b2927..4a3076e 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Android.bp
@@ -13,26 +13,32 @@
 // limitations under the License.
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion1",
+    name: "PackageManagerTestAppStub",
+    manifest: "AndroidManifestVersion1.xml",
+    srcs: []
+}
+
+android_test_helper_app {
+    name: "PackageManagerTestAppVersion1",
     manifest: "AndroidManifestVersion1.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion2",
+    name: "PackageManagerTestAppVersion2",
     manifest: "AndroidManifestVersion2.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion3",
+    name: "PackageManagerTestAppVersion3",
     manifest: "AndroidManifestVersion3.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppVersion4",
+    name: "PackageManagerTestAppVersion4",
     manifest: "AndroidManifestVersion4.xml"
 }
 
 android_test_helper_app {
-    name: "PackageManagerDummyAppOriginalOverride",
+    name: "PackageManagerTestAppOriginalOverride",
     manifest: "AndroidManifestOriginalOverride.xml"
 }
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
index f16e1bc..cba580e 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestOriginalOverride.xml
@@ -16,10 +16,10 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app.override"
+    package="com.android.server.pm.test.test_app.override"
     android:versionCode="2"
     >
 
-    <original-package android:name="com.android.server.pm.test.dummy_app"/>
+    <original-package android:name="com.android.server.pm.test.test_app"/>
 
 </manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
index b492a31..efc7372 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion1.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="1"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
index 25e9f8e..620054c 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion2.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="2"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
index 935f5e6..1997771 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion3.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="3"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
index d0643cb..d6ade03 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/AndroidManifestVersion4.xml
@@ -16,12 +16,12 @@
   -->
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.dummy_app"
+    package="com.android.server.pm.test.test_app"
     android:versionCode="4"
     >
 
     <permission
-        android:name="com.android.server.pm.test.dummy_app.TEST_PERMISSION"
+        android:name="com.android.server.pm.test.test_app.TEST_PERMISSION"
         android:protectionLevel="normal"
         />
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 2a267c4..1b2711d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -99,6 +99,7 @@
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.io.File;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
@@ -165,7 +166,7 @@
         setFieldValue(ActivityManagerService.class, sService, "mHandler",
                 mock(ActivityManagerService.MainHandler.class));
         setFieldValue(ActivityManagerService.class, sService, "mProcessStats",
-                mock(ProcessStatsService.class));
+                new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats")));
         setFieldValue(ActivityManagerService.class, sService, "mBackupTargets",
                 mock(SparseArray.class));
         setFieldValue(ActivityManagerService.class, sService, "mOomAdjProfiler",
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index f991dff..db56657 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -550,7 +550,6 @@
                 .setAppId(DUMMY_TARGET_APPID)
                 .setName("com.some.package")
                 .setCodePath("/")
-                .setResourcePath("/")
                 .setPVersionCode(1L)
                 .build();
         PackageSetting calling = simulateAddPackage(appsFilter,
@@ -874,7 +873,6 @@
                 .setAppId(appId)
                 .setName(newPkg.getPackageName())
                 .setCodePath("/")
-                .setResourcePath("/")
                 .setPVersionCode(1L);
         final PackageSetting setting =
                 (action == null ? settingBuilder : action.withBuilder(settingBuilder)).build();
diff --git a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
index 164bd72..90edaef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/KeySetManagerServiceTest.java
@@ -38,8 +38,7 @@
 
     public PackageSetting generateFakePackageSetting(String name) {
         return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
-                new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
-                "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
+                "", "", "", "", 1, 0, 0, 0 /*sharedUserId*/, null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/, null /*mimeGroups*/);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index c4e25b5..80f145b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -86,8 +86,7 @@
         // Create a real (non-null) PackageSetting and confirm that the removed
         // users are copied properly
         setting = new PackageSetting("name", "realName", new File("codePath"),
-                new File("resourcePath"), "legacyNativeLibraryPathString",
-                "primaryCpuAbiString", "secondaryCpuAbiString",
+                "legacyNativeLibraryPathString", "primaryCpuAbiString", "secondaryCpuAbiString",
                 "cpuAbiOverrideString", 0, 0, 0, 0,
                 null, null, null);
         pri.populateUsers(new int[] {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index aa92ba4..0bf06bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -439,7 +439,6 @@
                 PACKAGE_NAME,
                 REAL_PACKAGE_NAME,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -461,7 +460,6 @@
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -477,7 +475,6 @@
                 PACKAGE_NAME /*pkgName*/,
                 REAL_PACKAGE_NAME /*realPkgName*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 null /*primaryCpuAbiString*/,
                 null /*secondaryCpuAbiString*/,
@@ -507,7 +504,6 @@
                 null /*disabledPkg*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -541,7 +537,6 @@
                 null /*disabledPkg*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -581,7 +576,6 @@
                     null /*disabledPkg*/,
                     testUserSetting01 /*sharedUser*/,
                     UPDATED_CODE_PATH /*codePath*/,
-                    null /*resourcePath*/,
                     null /*legacyNativeLibraryPath*/,
                     "arm64-v8a" /*primaryCpuAbi*/,
                     "armeabi" /*secondaryCpuAbi*/,
@@ -609,7 +603,6 @@
                 null /*realPkgName*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -624,12 +617,11 @@
                 null /*usesStaticLibraries*/,
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
-        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         // signatures object must be different
         assertNotSame(testPkgSetting01.signatures, originalSignatures);
@@ -649,7 +641,6 @@
                 null /*realPkgName*/,
                 null /*sharedUser*/,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -665,12 +656,11 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(0));
-        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
-        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
         assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
         // by default, the package is considered stopped
@@ -695,7 +685,6 @@
                 null /*realPkgName*/,
                 testUserSetting01 /*sharedUser*/,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -711,12 +700,11 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(10064));
-        assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
-        assertThat(testPkgSetting01.resourcePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("x86"));
         assertThat(testPkgSetting01.versionCode, is(INITIAL_VERSION_CODE));
         final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -738,7 +726,6 @@
                 null /*realPkgName*/,
                 null /*sharedUser*/,
                 UPDATED_CODE_PATH /*codePath*/,
-                UPDATED_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPath*/,
                 "arm64-v8a" /*primaryCpuAbi*/,
                 "armeabi" /*secondaryCpuAbi*/,
@@ -754,12 +741,11 @@
                 null /*usesStaticLibrariesVersions*/,
                 null /*mimeGroups*/);
         assertThat(testPkgSetting01.appId, is(10064));
-        assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
+        assertThat(testPkgSetting01.getCodePath(), is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
-        assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
         assertNotSame(testPkgSetting01.signatures, disabledSignatures);
         assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
@@ -806,10 +792,10 @@
     private void verifySettingCopy(PackageSetting origPkgSetting, PackageSetting testPkgSetting) {
         assertThat(origPkgSetting, is(not(testPkgSetting)));
         assertThat(origPkgSetting.appId, is(testPkgSetting.appId));
-        assertSame(origPkgSetting.codePath, testPkgSetting.codePath);
-        assertThat(origPkgSetting.codePath, is(testPkgSetting.codePath));
-        assertSame(origPkgSetting.codePathString, testPkgSetting.codePathString);
-        assertThat(origPkgSetting.codePathString, is(testPkgSetting.codePathString));
+        assertSame(origPkgSetting.getCodePath(), testPkgSetting.getCodePath());
+        assertThat(origPkgSetting.getCodePath(), is(testPkgSetting.getCodePath()));
+        assertSame(origPkgSetting.getCodePathString(), testPkgSetting.getCodePathString());
+        assertThat(origPkgSetting.getCodePathString(), is(testPkgSetting.getCodePathString()));
         assertSame(origPkgSetting.cpuAbiOverrideString, testPkgSetting.cpuAbiOverrideString);
         assertThat(origPkgSetting.cpuAbiOverrideString, is(testPkgSetting.cpuAbiOverrideString));
         assertThat(origPkgSetting.firstInstallTime, is(testPkgSetting.firstInstallTime));
@@ -823,7 +809,9 @@
                 testPkgSetting.legacyNativeLibraryPathString);
         assertThat(origPkgSetting.legacyNativeLibraryPathString,
                 is(testPkgSetting.legacyNativeLibraryPathString));
-        assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+        if (origPkgSetting.mimeGroups != null) {
+            assertNotSame(origPkgSetting.mimeGroups, testPkgSetting.mimeGroups);
+        }
         assertThat(origPkgSetting.mimeGroups, is(testPkgSetting.mimeGroups));
         assertNotSame(origPkgSetting.mPermissionsState, testPkgSetting.mPermissionsState);
         assertThat(origPkgSetting.mPermissionsState, is(testPkgSetting.mPermissionsState));
@@ -839,10 +827,6 @@
         assertSame(origPkgSetting.primaryCpuAbiString, testPkgSetting.primaryCpuAbiString);
         assertThat(origPkgSetting.primaryCpuAbiString, is(testPkgSetting.primaryCpuAbiString));
         assertThat(origPkgSetting.realName, is(testPkgSetting.realName));
-        assertSame(origPkgSetting.resourcePath, testPkgSetting.resourcePath);
-        assertThat(origPkgSetting.resourcePath, is(testPkgSetting.resourcePath));
-        assertSame(origPkgSetting.resourcePathString, testPkgSetting.resourcePathString);
-        assertThat(origPkgSetting.resourcePathString, is(testPkgSetting.resourcePathString));
         assertSame(origPkgSetting.secondaryCpuAbiString, testPkgSetting.secondaryCpuAbiString);
         assertThat(origPkgSetting.secondaryCpuAbiString, is(testPkgSetting.secondaryCpuAbiString));
         assertSame(origPkgSetting.sharedUser, testPkgSetting.sharedUser);
@@ -874,7 +858,6 @@
                 PACKAGE_NAME,
                 REAL_PACKAGE_NAME,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
@@ -893,7 +876,6 @@
                 packageName,
                 packageName,
                 INITIAL_CODE_PATH /*codePath*/,
-                INITIAL_CODE_PATH /*resourcePath*/,
                 null /*legacyNativeLibraryPathString*/,
                 "x86_64" /*primaryCpuAbiString*/,
                 "x86" /*secondaryCpuAbiString*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index b0b5386..2651cfa 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -312,8 +312,7 @@
 
     private static PackageSetting mockPkgSetting(AndroidPackage pkg) {
         return new PackageSetting(pkg.getPackageName(), pkg.getRealPackage(),
-                new File(pkg.getCodePath()), new File(pkg.getCodePath()), null,
-                pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
+                new File(pkg.getCodePath()), null, pkg.getPrimaryCpuAbi(), pkg.getSecondaryCpuAbi(),
                 null, pkg.getVersionCode(),
                 PackageInfoUtils.appInfoFlags(pkg, null),
                 PackageInfoUtils.appInfoPrivateFlags(pkg, null),
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index d12ea894..f8e92ad 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -30,7 +30,6 @@
     private String mName;
     private String mRealName;
     private String mCodePath;
-    private String mResourcePath;
     private String mLegacyNativeLibraryPathString;
     private String mPrimaryCpuAbiString;
     private String mSecondaryCpuAbiString;
@@ -74,11 +73,6 @@
         return this;
     }
 
-    public PackageSettingBuilder setResourcePath(String resourcePath) {
-        this.mResourcePath = resourcePath;
-        return this;
-    }
-
     public PackageSettingBuilder setLegacyNativeLibraryPathString(
             String legacyNativeLibraryPathString) {
         this.mLegacyNativeLibraryPathString = legacyNativeLibraryPathString;
@@ -162,10 +156,10 @@
 
     public PackageSetting build() {
         final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
-                new File(mCodePath), new File(mResourcePath),
-                mLegacyNativeLibraryPathString, mPrimaryCpuAbiString, mSecondaryCpuAbiString,
-                mCpuAbiOverrideString, mPVersionCode, mPkgFlags, mPrivateFlags, mSharedUserId,
-                mUsesStaticLibraries, mUsesStaticLibrariesVersions, mMimeGroups);
+                new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
+                mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
+                mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
+                mMimeGroups);
         packageSetting.signatures = mSigningDetails != null
                 ? new PackageSignatures(mSigningDetails)
                 : new PackageSignatures();
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
index 55bc681..7108490 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -465,9 +465,9 @@
         // Generic PackageSetting object with values from a test app installed on a device to be
         // used to test the methods under the PackageSignatures signatures data member.
         File appPath = new File("/data/app/app");
-        PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
-                "/data/app/app", null, null, null,
-                1, 940097092, 0, 0 /*userId*/, null, null, null /*mimeGroups*/);
+        PackageSetting result = new PackageSetting("test.app", null, appPath,
+                "/data/app/app", null, null, null, 1, 940097092, 0, 0 /*userId*/, null, null,
+                null /*mimeGroups*/);
         return result;
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index e7eff00..56dddb0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -484,8 +484,7 @@
     private static PackageSettingBuilder createBasicPackageSettingBuilder(String packageName) {
         return new PackageSettingBuilder()
                 .setName(packageName)
-                .setCodePath(createCodePath(packageName))
-                .setResourcePath(createCodePath(packageName));
+                .setCodePath(createCodePath(packageName));
     }
 
     private static ScanRequestBuilder createBasicScanRequestBuilder(ParsingPackage pkg) {
@@ -534,8 +533,7 @@
                 arrayContaining("some.static.library", "some.other.static.library"));
         assertThat(pkgSetting.usesStaticLibrariesVersions, is(new long[]{234L, 456L}));
         assertThat(pkgSetting.pkg, is(scanResult.request.parsedPackage));
-        assertThat(pkgSetting.codePath, is(new File(createCodePath(packageName))));
-        assertThat(pkgSetting.resourcePath, is(new File(createCodePath(packageName))));
+        assertThat(pkgSetting.getCodePath(), is(new File(createCodePath(packageName))));
         assertThat(pkgSetting.versionCode, is(PackageInfo.composeLongVersionCode(1, 2345)));
     }
 
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 4040fa6..e3c795d 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -38,6 +38,7 @@
     <uses-permission android:name="android.permission.REORDER_TASKS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.STATUS_BAR" />
+    <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
 
     <!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
     <application android:debuggable="true"
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index addf1ff..96b9700 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -38,7 +38,13 @@
 
 import android.app.WaitResult;
 import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.media.ImageReader;
 import android.platform.test.annotations.Presubmit;
+import android.view.Display;
+import android.view.DisplayInfo;
 
 import androidx.test.filters.MediumTest;
 
@@ -173,4 +179,50 @@
         verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
                 eq(true) /* focused */);
     }
+
+    @Test
+    /** Ensures that a trusted virtual display can launch arbitrary activities. */
+    public void testTrustedVirtualDisplayCanLaunchActivities() {
+        final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+        final Task stack = new StackBuilder(mRootWindowContainer)
+                .setDisplay(newDisplay).build();
+        final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
+        VirtualDisplay virtualDisplay = createVirtualDisplay(true);
+        final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
+                virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+
+        assertThat(allowed).isTrue();
+    }
+
+    @Test
+    /** Ensures that an untrusted virtual display cannot launch arbitrary activities. */
+    public void testUntrustedVirtualDisplayCannotLaunchActivities() {
+        final DisplayContent newDisplay = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+        final Task stack = new StackBuilder(mRootWindowContainer)
+                .setDisplay(newDisplay).build();
+        final ActivityRecord unresizableActivity = stack.getTopNonFinishingActivity();
+        VirtualDisplay virtualDisplay = createVirtualDisplay(false);
+        final boolean allowed = mSupervisor.isCallerAllowedToLaunchOnDisplay(1234, 1234,
+                virtualDisplay.getDisplay().getDisplayId(), unresizableActivity.info);
+
+        assertThat(allowed).isFalse();
+    }
+
+    private VirtualDisplay createVirtualDisplay(boolean trusted) {
+        final DisplayManager dm = mContext.getSystemService(DisplayManager.class);
+        final DisplayInfo displayInfo = new DisplayInfo();
+        final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+        defaultDisplay.getDisplayInfo(displayInfo);
+        int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
+        if (trusted) {
+            flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
+        }
+
+        final ImageReader imageReader = ImageReader.newInstance(
+                displayInfo.logicalWidth, displayInfo.logicalHeight, PixelFormat.RGBA_8888, 2);
+
+        return dm.createVirtualDisplay("virtualDisplay", displayInfo.logicalWidth,
+                displayInfo.logicalHeight,
+                displayInfo.logicalDensityDpi, imageReader.getSurface(), flags);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index d950344..b4a1337 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
@@ -87,7 +88,7 @@
                 0 /* systemUiVisibility */, false /* isTranslucent */);
         mSurface = new TaskSnapshotSurface(mWm, new Window(), new SurfaceControl(), snapshot, "Test",
                 createTaskDescription(Color.WHITE, Color.RED, Color.BLUE), sysuiVis, windowFlags, 0,
-                taskBounds, ORIENTATION_PORTRAIT, new InsetsState());
+                taskBounds, ORIENTATION_PORTRAIT, ACTIVITY_TYPE_STANDARD, new InsetsState());
     }
 
     private static TaskDescription createTaskDescription(int background, int statusBar,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
index e078f26..c2ebcc2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/splitscreen/OpenAppToSplitScreenTest.kt
@@ -102,7 +102,8 @@
 
                 eventLog {
                     focusChanges(testApp.`package`,
-                            "recents_animation_input_consumer", "NexusLauncherActivity")
+                            "recents_animation_input_consumer", "NexusLauncherActivity",
+                            bugId = 151179149)
                 }
             }
         }
diff --git a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
index f30c35a..c2a5459 100644
--- a/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
+++ b/tests/utils/hostutils/src/com/android/internal/util/test/SystemPreparer.java
@@ -34,6 +34,8 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.ArrayList;
 
 import javax.annotation.Nullable;
@@ -49,7 +51,7 @@
 public class SystemPreparer extends ExternalResource {
     private static final long OVERLAY_ENABLE_TIMEOUT_MS = 30000;
 
-    // The paths of the files pushed onto the device through this rule.
+    // The paths of the files pushed onto the device through this rule to be removed after.
     private ArrayList<String> mPushedFiles = new ArrayList<>();
 
     // The package names of packages installed through this rule.
@@ -81,7 +83,7 @@
         final ITestDevice device = mDeviceProvider.getDevice();
         remount();
         assertTrue(device.pushFile(copyResourceToTemp(filePath), outputPath));
-        mPushedFiles.add(outputPath);
+        addPushedFile(device, outputPath);
         return this;
     }
 
@@ -91,10 +93,23 @@
         final ITestDevice device = mDeviceProvider.getDevice();
         remount();
         assertTrue(device.pushFile(file, outputPath));
-        mPushedFiles.add(outputPath);
+        addPushedFile(device, outputPath);
         return this;
     }
 
+    private void addPushedFile(ITestDevice device, String outputPath)
+            throws DeviceNotAvailableException {
+        Path pathCreated = Paths.get(outputPath);
+
+        // Find the top most parent that is new to the device
+        while (pathCreated.getParent() != null
+                && !device.doesFileExist(pathCreated.getParent().toString())) {
+            pathCreated = pathCreated.getParent();
+        }
+
+        mPushedFiles.add(pathCreated.toString());
+    }
+
     /** Deletes the given path from the device */
     public SystemPreparer deleteFile(String file) throws DeviceNotAvailableException {
         final ITestDevice device = mDeviceProvider.getDevice();
@@ -203,7 +218,7 @@
 
     /** Removes installed packages and files that were pushed to the device. */
     @Override
-    protected void after() {
+    public void after() {
         final ITestDevice device = mDeviceProvider.getDevice();
         try {
             remount();