Merge "Added UserManagerInternal.getUserAssignedToDisplay(displayId)."
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 877e7d3..6f4bb45 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -145,7 +145,10 @@
      * that it is preserved through activty destroy and restore.
      */
     private ArrayList<String> getPendingExitNames() {
-        if (mPendingExitNames == null && mEnterTransitionCoordinator != null) {
+        if (mPendingExitNames == null
+                && mEnterTransitionCoordinator != null
+                && !mEnterTransitionCoordinator.isReturning()
+        ) {
             mPendingExitNames = mEnterTransitionCoordinator.getPendingExitSharedElementNames();
         }
         return mPendingExitNames;
@@ -202,6 +205,7 @@
             restoreExitedViews();
             activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
         }
+        getPendingExitNames(); // Set mPendingExitNames before resetting mEnterTransitionCoordinator
         mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
                 resultReceiver, sharedElementNames, mEnterActivityOptions.isReturning(),
                 mEnterActivityOptions.isCrossTask());
@@ -250,6 +254,7 @@
     public void onStop(Activity activity) {
         restoreExitedViews();
         if (mEnterTransitionCoordinator != null) {
+            getPendingExitNames(); // Set mPendingExitNames before clearing
             mEnterTransitionCoordinator.stop();
             mEnterTransitionCoordinator = null;
         }
@@ -275,6 +280,7 @@
                         restoreReenteringViews();
                     } else if (mEnterTransitionCoordinator.isReturning()) {
                         mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
+                            getPendingExitNames(); // Set mPendingExitNames before clearing
                             mEnterTransitionCoordinator = null;
                         });
                     }
@@ -374,6 +380,7 @@
     }
 
     public void startExitOutTransition(Activity activity, Bundle options) {
+        getPendingExitNames(); // Set mPendingExitNames before clearing mEnterTransitionCoordinator
         mEnterTransitionCoordinator = null;
         if (!activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS) ||
                 mExitTransitionCoordinators == null) {
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 18d86d6..1c4898a 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -882,6 +882,7 @@
             }
 
             // Indicate if the discontinuity count changed
+            t.firstEventAfterDiscontinuity = false;
             if (t.sensor.getType() == Sensor.TYPE_HEAD_TRACKER) {
                 final int lastCount = mSensorDiscontinuityCounts.get(handle);
                 final int curCount = Float.floatToIntBits(values[6]);
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 2d4da2c..a52fc75 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -180,9 +180,9 @@
     private static native void nativeClear(long ptr);
     private static native void nativeAddMovement(long ptr, MotionEvent event);
     private static native void nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity);
-    private static native float nativeGetXVelocity(long ptr, int id);
-    private static native float nativeGetYVelocity(long ptr, int id);
-    private static native boolean nativeGetEstimator(long ptr, int id, Estimator outEstimator);
+    private static native float nativeGetVelocity(long ptr, int axis, int id);
+    private static native boolean nativeGetEstimator(
+            long ptr, int axis, int id, Estimator outEstimator);
 
     static {
         // Strategy string and IDs mapping lookup.
@@ -361,7 +361,7 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity() {
-        return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID);
+        return getXVelocity(ACTIVE_POINTER_ID);
     }
 
     /**
@@ -371,7 +371,7 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity() {
-        return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID);
+        return getYVelocity(ACTIVE_POINTER_ID);
     }
 
     /**
@@ -382,7 +382,7 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity(int id) {
-        return nativeGetXVelocity(mPtr, id);
+        return nativeGetVelocity(mPtr, MotionEvent.AXIS_X, id);
     }
 
     /**
@@ -393,7 +393,7 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity(int id) {
-        return nativeGetYVelocity(mPtr, id);
+        return nativeGetVelocity(mPtr, MotionEvent.AXIS_Y, id);
     }
 
     /**
@@ -403,6 +403,8 @@
      * It is not necessary to call {@link #computeCurrentVelocity(int)} before calling
      * this method.
      *
+     * @param axis Which axis's velocity to return.
+     *             Should be one of the axes defined in {@link MotionEvent}.
      * @param id Which pointer's velocity to return.
      * @param outEstimator The estimator to populate.
      * @return True if an estimator was obtained, false if there is no information
@@ -410,11 +412,11 @@
      *
      * @hide For internal use only.  Not a final API.
      */
-    public boolean getEstimator(int id, Estimator outEstimator) {
+    public boolean getEstimator(int axis, int id, Estimator outEstimator) {
         if (outEstimator == null) {
             throw new IllegalArgumentException("outEstimator must not be null");
         }
-        return nativeGetEstimator(mPtr, id, outEstimator);
+        return nativeGetEstimator(mPtr, axis, id, outEstimator);
     }
 
     /**
@@ -434,16 +436,9 @@
         private static final int MAX_DEGREE = 4;
 
         /**
-         * Polynomial coefficients describing motion in X.
+         * Polynomial coefficients describing motion.
          */
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public final float[] xCoeff = new float[MAX_DEGREE + 1];
-
-        /**
-         * Polynomial coefficients describing motion in Y.
-         */
-        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-        public final float[] yCoeff = new float[MAX_DEGREE + 1];
+        public final float[] coeff = new float[MAX_DEGREE + 1];
 
         /**
          * Polynomial degree, or zero if only position information is available.
@@ -458,39 +453,21 @@
         public float confidence;
 
         /**
-         * Gets an estimate of the X position of the pointer at the specified time point.
+         * Gets an estimate of the position of the pointer at the specified time point.
          * @param time The time point in seconds, 0 is the last recorded time.
-         * @return The estimated X coordinate.
+         * @return The estimated axis value.
          */
-        public float estimateX(float time) {
-            return estimate(time, xCoeff);
+        public float estimate(float time) {
+            return estimate(time, coeff);
         }
 
         /**
-         * Gets an estimate of the Y position of the pointer at the specified time point.
-         * @param time The time point in seconds, 0 is the last recorded time.
-         * @return The estimated Y coordinate.
-         */
-        public float estimateY(float time) {
-            return estimate(time, yCoeff);
-        }
-
-        /**
-         * Gets the X coefficient with the specified index.
+         * Gets the coefficient with the specified index.
          * @param index The index of the coefficient to return.
-         * @return The X coefficient, or 0 if the index is greater than the degree.
+         * @return The coefficient, or 0 if the index is greater than the degree.
          */
-        public float getXCoeff(int index) {
-            return index <= degree ? xCoeff[index] : 0;
-        }
-
-        /**
-         * Gets the Y coefficient with the specified index.
-         * @param index The index of the coefficient to return.
-         * @return The Y coefficient, or 0 if the index is greater than the degree.
-         */
-        public float getYCoeff(int index) {
-            return index <= degree ? yCoeff[index] : 0;
+        public float getCoeff(int index) {
+            return index <= degree ? coeff[index] : 0;
         }
 
         private float estimate(float time, float[] c) {
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 9ee9b82..80d50ff 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -90,8 +90,10 @@
         private float mBoundingBottom;
 
         // Position estimator.
-        private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
-        private VelocityTracker.Estimator mAltEstimator = new VelocityTracker.Estimator();
+        private VelocityTracker.Estimator mEstimatorX = new VelocityTracker.Estimator();
+        private VelocityTracker.Estimator mAltEstimatorX = new VelocityTracker.Estimator();
+        private VelocityTracker.Estimator mEstimatorY = new VelocityTracker.Estimator();
+        private VelocityTracker.Estimator mAltEstimatorY = new VelocityTracker.Estimator();
 
         @UnsupportedAppUsage
         public PointerState() {
@@ -679,11 +681,13 @@
                 ps.addTrace(coords.x, coords.y, true);
                 ps.mXVelocity = mVelocity.getXVelocity(id);
                 ps.mYVelocity = mVelocity.getYVelocity(id);
-                mVelocity.getEstimator(id, ps.mEstimator);
+                mVelocity.getEstimator(MotionEvent.AXIS_X, id, ps.mEstimatorX);
+                mVelocity.getEstimator(MotionEvent.AXIS_Y, id, ps.mEstimatorY);
                 if (mAltVelocity != null) {
                     ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
                     ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
-                    mAltVelocity.getEstimator(id, ps.mAltEstimator);
+                    mAltVelocity.getEstimator(MotionEvent.AXIS_X, id, ps.mAltEstimatorX);
+                    mAltVelocity.getEstimator(MotionEvent.AXIS_Y, id, ps.mAltEstimatorY);
                 }
                 ps.mToolType = event.getToolType(i);
 
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 46bd682..16b9f00 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -32,8 +32,7 @@
 static const int ACTIVE_POINTER_ID = -1;
 
 static struct {
-    jfieldID xCoeff;
-    jfieldID yCoeff;
+    jfieldID coeff;
     jfieldID degree;
     jfieldID confidence;
 } gEstimatorClassInfo;
@@ -47,28 +46,22 @@
 
     void clear();
     void addMovement(const MotionEvent* event);
+    // TODO(b/32830165): consider supporting an overload that supports computing velocity only for
+    // a subset of the supported axes.
     void computeCurrentVelocity(int32_t units, float maxVelocity);
-    void getVelocity(int32_t id, float* outVx, float* outVy);
-    bool getEstimator(int32_t id, VelocityTracker::Estimator* outEstimator);
+    float getVelocity(int32_t axis, int32_t id);
+    bool getEstimator(int32_t axis, int32_t id, VelocityTracker::Estimator* outEstimator);
 
 private:
-    struct Velocity {
-        float vx, vy;
-    };
-
     VelocityTracker mVelocityTracker;
-    int32_t mActivePointerId;
-    BitSet32 mCalculatedIdBits;
-    Velocity mCalculatedVelocity[MAX_POINTERS];
+    VelocityTracker::ComputedVelocity mComputedVelocity;
 };
 
 VelocityTrackerState::VelocityTrackerState(const VelocityTracker::Strategy strategy)
-      : mVelocityTracker(strategy), mActivePointerId(-1) {}
+      : mVelocityTracker(strategy) {}
 
 void VelocityTrackerState::clear() {
     mVelocityTracker.clear();
-    mActivePointerId = -1;
-    mCalculatedIdBits.clear();
 }
 
 void VelocityTrackerState::addMovement(const MotionEvent* event) {
@@ -76,61 +69,20 @@
 }
 
 void VelocityTrackerState::computeCurrentVelocity(int32_t units, float maxVelocity) {
-    BitSet32 idBits(mVelocityTracker.getCurrentPointerIdBits());
-    mCalculatedIdBits = idBits;
-
-    for (uint32_t index = 0; !idBits.isEmpty(); index++) {
-        uint32_t id = idBits.clearFirstMarkedBit();
-
-        float vx, vy;
-        mVelocityTracker.getVelocity(id, &vx, &vy);
-
-        vx = vx * units / 1000;
-        vy = vy * units / 1000;
-
-        if (vx > maxVelocity) {
-            vx = maxVelocity;
-        } else if (vx < -maxVelocity) {
-            vx = -maxVelocity;
-        }
-        if (vy > maxVelocity) {
-            vy = maxVelocity;
-        } else if (vy < -maxVelocity) {
-            vy = -maxVelocity;
-        }
-
-        Velocity& velocity = mCalculatedVelocity[index];
-        velocity.vx = vx;
-        velocity.vy = vy;
-    }
+    mComputedVelocity = mVelocityTracker.getComputedVelocity(units, maxVelocity);
 }
 
-void VelocityTrackerState::getVelocity(int32_t id, float* outVx, float* outVy) {
+float VelocityTrackerState::getVelocity(int32_t axis, int32_t id) {
     if (id == ACTIVE_POINTER_ID) {
         id = mVelocityTracker.getActivePointerId();
     }
 
-    float vx, vy;
-    if (id >= 0 && id <= MAX_POINTER_ID && mCalculatedIdBits.hasBit(id)) {
-        uint32_t index = mCalculatedIdBits.getIndexOfBit(id);
-        const Velocity& velocity = mCalculatedVelocity[index];
-        vx = velocity.vx;
-        vy = velocity.vy;
-    } else {
-        vx = 0;
-        vy = 0;
-    }
-
-    if (outVx) {
-        *outVx = vx;
-    }
-    if (outVy) {
-        *outVy = vy;
-    }
+    return mComputedVelocity.getVelocity(axis, id).value_or(0);
 }
 
-bool VelocityTrackerState::getEstimator(int32_t id, VelocityTracker::Estimator* outEstimator) {
-    return mVelocityTracker.getEstimator(id, outEstimator);
+bool VelocityTrackerState::getEstimator(int32_t axis, int32_t id,
+                                        VelocityTracker::Estimator* outEstimator) {
+    return mVelocityTracker.getEstimator(axis, id, outEstimator);
 }
 
 // Return a strategy enum from integer value.
@@ -177,37 +129,25 @@
     state->computeCurrentVelocity(units, maxVelocity);
 }
 
-static jfloat android_view_VelocityTracker_nativeGetXVelocity(JNIEnv* env, jclass clazz,
-        jlong ptr, jint id) {
+static jfloat android_view_VelocityTracker_nativeGetVelocity(JNIEnv* env, jclass clazz, jlong ptr,
+                                                             jint axis, jint id) {
     VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
-    float vx;
-    state->getVelocity(id, &vx, NULL);
-    return vx;
-}
-
-static jfloat android_view_VelocityTracker_nativeGetYVelocity(JNIEnv* env, jclass clazz,
-        jlong ptr, jint id) {
-    VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
-    float vy;
-    state->getVelocity(id, NULL, &vy);
-    return vy;
+    return state->getVelocity(axis, id);
 }
 
 static jboolean android_view_VelocityTracker_nativeGetEstimator(JNIEnv* env, jclass clazz,
-        jlong ptr, jint id, jobject outEstimatorObj) {
+                                                                jlong ptr, jint axis, jint id,
+                                                                jobject outEstimatorObj) {
     VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
     VelocityTracker::Estimator estimator;
-    bool result = state->getEstimator(id, &estimator);
 
-    jfloatArray xCoeffObj = jfloatArray(env->GetObjectField(outEstimatorObj,
-            gEstimatorClassInfo.xCoeff));
-    jfloatArray yCoeffObj = jfloatArray(env->GetObjectField(outEstimatorObj,
-            gEstimatorClassInfo.yCoeff));
+    bool result = state->getEstimator(axis, id, &estimator);
 
-    env->SetFloatArrayRegion(xCoeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
-            estimator.xCoeff);
-    env->SetFloatArrayRegion(yCoeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
-            estimator.yCoeff);
+    jfloatArray coeffObj =
+            jfloatArray(env->GetObjectField(outEstimatorObj, gEstimatorClassInfo.coeff));
+
+    env->SetFloatArrayRegion(coeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
+                             estimator.coeff);
     env->SetIntField(outEstimatorObj, gEstimatorClassInfo.degree, estimator.degree);
     env->SetFloatField(outEstimatorObj, gEstimatorClassInfo.confidence, estimator.confidence);
     return result;
@@ -224,9 +164,8 @@
          (void*)android_view_VelocityTracker_nativeAddMovement},
         {"nativeComputeCurrentVelocity", "(JIF)V",
          (void*)android_view_VelocityTracker_nativeComputeCurrentVelocity},
-        {"nativeGetXVelocity", "(JI)F", (void*)android_view_VelocityTracker_nativeGetXVelocity},
-        {"nativeGetYVelocity", "(JI)F", (void*)android_view_VelocityTracker_nativeGetYVelocity},
-        {"nativeGetEstimator", "(JILandroid/view/VelocityTracker$Estimator;)Z",
+        {"nativeGetVelocity", "(JII)F", (void*)android_view_VelocityTracker_nativeGetVelocity},
+        {"nativeGetEstimator", "(JIILandroid/view/VelocityTracker$Estimator;)Z",
          (void*)android_view_VelocityTracker_nativeGetEstimator},
 };
 
@@ -236,8 +175,7 @@
 
     jclass clazz = FindClassOrDie(env, "android/view/VelocityTracker$Estimator");
 
-    gEstimatorClassInfo.xCoeff = GetFieldIDOrDie(env, clazz, "xCoeff", "[F");
-    gEstimatorClassInfo.yCoeff = GetFieldIDOrDie(env, clazz, "yCoeff", "[F");
+    gEstimatorClassInfo.coeff = GetFieldIDOrDie(env, clazz, "coeff", "[F");
     gEstimatorClassInfo.degree = GetFieldIDOrDie(env, clazz, "degree", "I");
     gEstimatorClassInfo.confidence = GetFieldIDOrDie(env, clazz, "confidence", "F");
 
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
new file mode 100644
index 0000000..b006615
--- /dev/null
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SlowUserQueryDetector.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Checks for slow calls to ActivityManager.getCurrentUser() or UserManager.getUserInfo() and
+ * suggests using UserTracker instead. For more info, see: http://go/multi-user-in-systemui-slides.
+ */
+@Suppress("UnstableApiUsage")
+class SlowUserQueryDetector : Detector(), SourceCodeScanner {
+
+    override fun getApplicableMethodNames(): List<String> {
+        return listOf("getCurrentUser", "getUserInfo")
+    }
+
+    override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+        val evaluator = context.evaluator
+        if (
+            evaluator.isStatic(method) &&
+                method.name == "getCurrentUser" &&
+                method.containingClass?.qualifiedName == "android.app.ActivityManager"
+        ) {
+            context.report(
+                ISSUE_SLOW_USER_ID_QUERY,
+                method,
+                context.getNameLocation(node),
+                "ActivityManager.getCurrentUser() is slow. " +
+                    "Use UserTracker.getUserId() instead."
+            )
+        }
+        if (
+            !evaluator.isStatic(method) &&
+                method.name == "getUserInfo" &&
+                method.containingClass?.qualifiedName == "android.os.UserManager"
+        ) {
+            context.report(
+                ISSUE_SLOW_USER_INFO_QUERY,
+                method,
+                context.getNameLocation(node),
+                "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+            )
+        }
+    }
+
+    companion object {
+        @JvmField
+        val ISSUE_SLOW_USER_ID_QUERY: Issue =
+            Issue.create(
+                id = "SlowUserIdQuery",
+                briefDescription = "User ID queried using ActivityManager instead of UserTracker.",
+                explanation =
+                    "ActivityManager.getCurrentUser() makes a binder call and is slow. " +
+                        "Instead, inject a UserTracker and call UserTracker.getUserId(). For " +
+                        "more info, see: http://go/multi-user-in-systemui-slides",
+                category = Category.PERFORMANCE,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+            )
+
+        @JvmField
+        val ISSUE_SLOW_USER_INFO_QUERY: Issue =
+            Issue.create(
+                id = "SlowUserInfoQuery",
+                briefDescription = "User info queried using UserManager instead of UserTracker.",
+                explanation =
+                    "UserManager.getUserInfo() makes a binder call and is slow. " +
+                        "Instead, inject a UserTracker and call UserTracker.getUserInfo(). For " +
+                        "more info, see: http://go/multi-user-in-systemui-slides",
+                category = Category.PERFORMANCE,
+                priority = 8,
+                severity = Severity.WARNING,
+                implementation =
+                    Implementation(SlowUserQueryDetector::class.java, Scope.JAVA_FILE_SCOPE)
+            )
+    }
+}
diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
index c7c73d3..4879883 100644
--- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
+++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt
@@ -30,6 +30,8 @@
         get() = listOf(
                 BindServiceViaContextDetector.ISSUE,
                 BroadcastSentViaContextDetector.ISSUE,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY,
                 GetMainLooperViaContextDetector.ISSUE,
                 RegisterReceiverViaContextDetector.ISSUE,
                 SoftwareBitmapDetector.ISSUE,
diff --git a/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
new file mode 100644
index 0000000..2738f04
--- /dev/null
+++ b/packages/SystemUI/checks/tests/com/android/systemui/lint/SlowUserQueryDetectorTest.kt
@@ -0,0 +1,194 @@
+package com.android.internal.systemui.lint
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestFiles
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+import org.junit.Test
+
+class SlowUserQueryDetectorTest : LintDetectorTest() {
+
+    override fun getDetector(): Detector = SlowUserQueryDetector()
+    override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+    override fun getIssues(): List<Issue> =
+        listOf(
+            SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+            SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+        )
+
+    @Test
+    fun testGetCurrentUser() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.app.ActivityManager;
+
+                        public class TestClass1 {
+                            public void slewlyGetCurrentUser() {
+                                ActivityManager.getCurrentUser();
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(
+                SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+            )
+            .run()
+            .expectWarningCount(1)
+            .expectContains(
+                "ActivityManager.getCurrentUser() is slow. " +
+                    "Use UserTracker.getUserId() instead."
+            )
+    }
+
+    @Test
+    fun testGetUserInfo() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import android.os.UserManager;
+
+                        public class TestClass2 {
+                            public void slewlyGetUserInfo(UserManager userManager) {
+                                userManager.getUserInfo();
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(
+                SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+            )
+            .run()
+            .expectWarningCount(1)
+            .expectContains(
+                "UserManager.getUserInfo() is slow. " + "Use UserTracker.getUserInfo() instead."
+            )
+    }
+
+    @Test
+    fun testUserTrackerGetUserId() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import com.android.systemui.settings.UserTracker;
+
+                        public class TestClass3 {
+                            public void quicklyGetUserId(UserTracker userTracker) {
+                                userTracker.getUserId();
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(
+                SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+            )
+            .run()
+            .expectClean()
+    }
+
+    @Test
+    fun testUserTrackerGetUserInfo() {
+        lint()
+            .files(
+                TestFiles.java(
+                        """
+                        package test.pkg;
+                        import com.android.systemui.settings.UserTracker;
+
+                        public class TestClass4 {
+                            public void quicklyGetUserId(UserTracker userTracker) {
+                                userTracker.getUserInfo();
+                            }
+                        }
+                        """
+                    )
+                    .indented(),
+                *stubs
+            )
+            .issues(
+                SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY,
+                SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY
+            )
+            .run()
+            .expectClean()
+    }
+
+    private val activityManagerStub: TestFile =
+        java(
+            """
+            package android.app;
+
+            public class ActivityManager {
+                public static int getCurrentUser() {};
+            }
+            """
+        )
+
+    private val userManagerStub: TestFile =
+        java(
+            """
+            package android.os;
+            import android.content.pm.UserInfo;
+            import android.annotation.UserIdInt;
+
+            public class UserManager {
+                public UserInfo getUserInfo(@UserIdInt int userId) {};
+            }
+            """
+        )
+
+    private val userIdIntStub: TestFile =
+        java(
+            """
+            package android.annotation;
+
+            public @interface UserIdInt {}
+            """
+        )
+
+    private val userInfoStub: TestFile =
+        java(
+            """
+            package android.content.pm;
+
+            public class UserInfo {}
+            """
+        )
+
+    private val userTrackerStub: TestFile =
+        java(
+            """
+            package com.android.systemui.settings;
+            import android.content.pm.UserInfo;
+
+            public interface UserTracker {
+                public int getUserId();
+                public UserInfo getUserInfo();
+            }
+            """
+        )
+
+    private val stubs =
+        arrayOf(activityManagerStub, userManagerStub, userIdIntStub, userInfoStub, userTrackerStub)
+}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index c375c73..a1adc6f4 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -35,7 +35,6 @@
 
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.pm.Computer;
-import com.android.server.pm.pkg.PackageStateInternal;
 import com.android.server.pm.snapshot.PackageDataSnapshot;
 
 import java.io.PrintWriter;
@@ -566,7 +565,7 @@
      * "stopped", that is whether it should not be included in the result
      * if the intent requests to excluded stopped objects.
      */
-    protected boolean isFilterStopped(PackageStateInternal packageState, @UserIdInt int userId) {
+    protected boolean isFilterStopped(@NonNull Computer computer, F filter, @UserIdInt int userId) {
         return false;
     }
 
@@ -805,8 +804,7 @@
             int match;
             if (debug) Slog.v(TAG, "Matching against filter " + filter);
 
-            if (excludingStopped && isFilterStopped(computer.getPackageStateInternal(packageName),
-                    userId)) {
+            if (excludingStopped && isFilterStopped(computer, filter, userId)) {
                 if (debug) {
                     Slog.v(TAG, "  Filter's target is stopped; skipping");
                 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
index 4779f6f..6a2731d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceRegistry.java
@@ -216,19 +216,31 @@
             return null;
         }
 
-        if (mAllProps.size() > 1) {
-            Slog.e(TAG, "getSingleProvider() called but multiple sensors present: "
-                    + mAllProps.size());
-        }
+        // TODO(b/242837110): remove the try-catch once the bug is fixed.
+        try {
+            if (mAllProps.size() > 1) {
+                Slog.e(TAG, "getSingleProvider() called but multiple sensors present: "
+                        + mAllProps.size());
+            }
 
-        final int sensorId = mAllProps.get(0).sensorId;
-        final T provider = getProviderForSensor(sensorId);
-        if (provider != null) {
-            return new Pair<>(sensorId, provider);
-        }
+            final int sensorId = mAllProps.get(0).sensorId;
+            final T provider = getProviderForSensor(sensorId);
+            if (provider != null) {
+                return new Pair<>(sensorId, provider);
+            }
 
-        Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found");
-        return null;
+            Slog.e(TAG, "Single sensor: " + sensorId + ", but provider not found");
+            return null;
+        } catch (NullPointerException e) {
+            final String extra;
+            if (mAllProps == null) {
+                extra = "mAllProps: null";
+            } else {
+                extra = "mAllProps.size(): " + mAllProps.size();
+            }
+            Slog.e(TAG, "This shouldn't happen. " + extra, e);
+            throw e;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 9db9837..8024d41 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -23,7 +23,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION_GROUP;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
@@ -1420,9 +1419,7 @@
         }
 
         if (!isApex) {
-            if (!doRenameLI(args, res.mReturnCode, parsedPackage)) {
-                throw new PrepareFailure(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
-            }
+            doRenameLI(args, res.mReturnCode, res.mReturnMsg, parsedPackage);
 
             try {
                 setUpFsVerityIfPossible(parsedPackage);
@@ -1689,19 +1686,20 @@
      * scanned package should be updated to reflect the rename.
      */
     @GuardedBy("mPm.mInstallLock")
-    private boolean doRenameLI(InstallArgs args, int status, ParsedPackage parsedPackage) {
+    private void doRenameLI(InstallArgs args, int status, String statusMsg,
+            ParsedPackage parsedPackage) throws PrepareFailure {
         if (args.mMoveInfo != null) {
             if (status != PackageManager.INSTALL_SUCCEEDED) {
                 mRemovePackageHelper.cleanUpForMoveInstall(args.mMoveInfo.mToUuid,
                         args.mMoveInfo.mPackageName, args.mMoveInfo.mFromCodePath);
-                return false;
+                throw new PrepareFailure(status, statusMsg);
             }
-            return true;
+            return;
         }
         // For file installations
         if (status != PackageManager.INSTALL_SUCCEEDED) {
             mRemovePackageHelper.removeCodePath(args.mCodeFile);
-            return false;
+            throw new PrepareFailure(status, statusMsg);
         }
 
         final File targetDir = resolveTargetDir(args);
@@ -1723,12 +1721,14 @@
             }
         } catch (IOException | ErrnoException e) {
             Slog.w(TAG, "Failed to rename", e);
-            return false;
+            throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE,
+                    "Failed to rename");
         }
 
         if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) {
             Slog.w(TAG, "Failed to restorecon");
-            return false;
+            throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                    "Failed to restorecon");
         }
 
         // Reflect the rename internally
@@ -1739,14 +1739,13 @@
             parsedPackage.setPath(afterCodeFile.getCanonicalPath());
         } catch (IOException e) {
             Slog.e(TAG, "Failed to get path: " + afterCodeFile, e);
-            return false;
+            throw new PrepareFailure(PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE,
+                    "Failed to get path: " + afterCodeFile);
         }
         parsedPackage.setBaseApkPath(FileUtils.rewriteAfterRename(beforeCodeFile,
                 afterCodeFile, parsedPackage.getBaseApkPath()));
         parsedPackage.setSplitCodePaths(FileUtils.rewriteAfterRename(beforeCodeFile,
                 afterCodeFile, parsedPackage.getSplitCodePaths()));
-
-        return true;
     }
 
     // TODO(b/168126411): Once staged install flow starts using the same folder as non-staged
diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
index 7baec62..1d95e87 100644
--- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
+++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java
@@ -930,12 +930,14 @@
         }
 
         @Override
-        protected boolean isFilterStopped(@Nullable PackageStateInternal packageState,
+        protected boolean isFilterStopped(@NonNull Computer computer, F filter,
                 @UserIdInt int userId) {
             if (!mUserManager.exists(userId)) {
                 return true;
             }
 
+            final PackageStateInternal packageState = computer.getPackageStateInternal(
+                    filter.first.getPackageName());
             if (packageState == null || packageState.getPkg() == null) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index be80118..16b5ee5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2120,7 +2120,7 @@
 
         mActivityRecordInputSink = new ActivityRecordInputSink(this, sourceRecord);
 
-        updateEnterpriseThumbnailDrawable(mAtmService.mUiContext);
+        updateEnterpriseThumbnailDrawable(mAtmService.getUiContext());
     }
 
     /**
@@ -7424,7 +7424,7 @@
         }
         final Rect frame = win.getRelativeFrame();
         final Drawable thumbnailDrawable = task.mUserId == mWmService.mCurrentUserId
-                ? mAtmService.mUiContext.getDrawable(R.drawable.ic_account_circle)
+                ? mAtmService.getUiContext().getDrawable(R.drawable.ic_account_circle)
                 : mEnterpriseThumbnailDrawable;
         final HardwareBuffer thumbnail = getDisplayContent().mAppTransition
                 .createCrossProfileAppsThumbnail(thumbnailDrawable, frame);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index bdbe787..8f5d838 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -345,7 +345,7 @@
      * This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
      * change at runtime. Use mContext for non-UI purposes.
      */
-    final Context mUiContext;
+    private final Context mUiContext;
     final ActivityThread mSystemThread;
     H mH;
     UiHandler mUiHandler;
@@ -1040,6 +1040,10 @@
         }
     }
 
+    Context getUiContext() {
+        return mUiContext;
+    }
+
     UserManagerService getUserManager() {
         if (mUserManager == null) {
             IBinder b = ServiceManager.getService(Context.USER_SERVICE);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 537b04d..e780606 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -421,7 +421,7 @@
         mService = service;
         mContext = displayContent.isDefaultDisplay ? service.mContext
                 : service.mContext.createDisplayContext(displayContent.getDisplay());
-        mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.mUiContext
+        mUiContext = displayContent.isDefaultDisplay ? service.mAtmService.getUiContext()
                 : service.mAtmService.mSystemThread
                         .getSystemUiContext(displayContent.getDisplayId());
         mDisplayContent = displayContent;