Merge "In SkSL, replace 'sample' with '.eval'"
diff --git a/apex/blobstore/README.md b/apex/blobstore/README.md
new file mode 100644
index 0000000..69af436
--- /dev/null
+++ b/apex/blobstore/README.md
@@ -0,0 +1,125 @@
+<!--
+  Copyright (C) 2021 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
+-->
+
+# BlobStore Manager
+
+## Introduction
+* BlobStoreManager is a system service added in Android R release that facilitates sharing of
+  data blobs among apps.
+* Apps that would like to share data blobs with other apps can do so by contributing those
+  data blobs with the System and can choose how they would like the System to share the data blobs
+  with other apps.
+* Apps can access data blobs shared by other apps from the System using checksum of the data blobs
+  (plus some other data attributes. More details [below](#blob-handle)).
+* The APIs provided by the BlobStoreManager are meant to reduce storage and network usage by
+  reusing the data available on the device instead of downloading the same data again and having
+  multiple copies of the same data on disk.
+* It is not meant to provide access to the data which apps otherwise would not be able to access.
+  In other words, if an app’s only means of obtaining access to certain data is through
+  BlobStoreManager, then that use case is not really intended or supported.
+* For example, if earlier an app was downloading certain shared data from a server, then by using
+  BlobStoreManager, it can first check whether or not the data is already available on the device
+  before downloading.
+
+## Concepts
+### Blob handle
+Blob handle is the identifier of the data and it is what apps need to use for referring to the
+data blobs. Currently, this is made of following bits of information:
+* SHA256 checksum of data
+* Data label: A user readable string that indicates what the data blob is.
+  This is meant to be used when surfacing a list of blobs to the user.
+* Data expiry time: A timestamp after which the data blob should be considered invalid and not
+  allowed to be accessed by any app.
+* Data tag: An opaque string associated with the blob. System does not interpret this in any way or
+  use it for any purposes other than when checking whether two Blob handle identifiers are referring
+  to the same data blob. This is meant to be used by the apps, either for categorization for
+  data blobs or for adding additional identifiers. For example, an app can add tags like
+  *machine_learning* or *media* depending on the data blob if necessary.
+
+When comparing two Blob handles, the System will compare all the pieces of information above and
+only when two Blob handles are equal, the data blobs corresponding to those identifiers are
+considered equal.
+
+### Blob sharing session
+Session is a way to allow apps to contribute data over multiple time intervals. Each session is
+associated with a unique Identifier that is created and obtained by the apps by calling
+[BlobStoreManager#createSession](https://developer.android.com/reference/android/app/blob/BlobStoreManager#createSession(android.app.blob.BlobHandle)).
+Apps can save the Identifier associated with a session and use it to open and close it
+multiple times for contributing the data. For example, if an app is downloading
+some content over the network, it can start a Session and start contributing this data to the
+System immediately and if the network connection is lost for any reason, the app can close this
+session. When the download resumes, the app can reopen the session and start contributing again.
+Note that once the entire data is contributed, the app has no reason to hold on to the Session Id.
+
+### Blob commit
+Since a data blob can be contributed in a session over multiple time intervals, an app closing a
+session does not imply that the contribution is completed. So, *commit* is added as an explicit
+event / signal for the app to indicate that the contribution of the data blob is completed.
+At this point, the System can verify the data blob does indeed correspond to the Blob handle used
+by the app and prevent the app from making any further modifications to the data blob. Once the
+data blob is committed and verified by the System, it is available for other applications to access.
+
+### Access modes
+When an application contributes a data blob to the System, it can choose to specify how it would
+like the System to share this data blob with other applications. Access modes refer to the type of
+access that apps specified when contributing a data blob. As of Android S release, there are
+four access modes:
+* Allow specific packages: Apps can specify a specific set of applications that are allowed to
+  access their data blob.
+* Allow packages with the same signature: Apps can specify that only the applications that are
+  signed with the same certificate as them can access their data blob.
+* Allow public access: Apps can specify that any other app on the device can access their data blob.
+* Allow private access: Apps can specify that no other app can access their data blob unless they
+  happen to contribute the same data blob.
+  * Note that in this case, two apps might download the same data blob and contribute to the System
+    in which case we are not saving anything in terms of bandwidth usage, but we would still be
+    saving disk usage since we would be keeping only one copy of data on disk.
+
+### Lease
+Leasing a blob is a way to specify that an application is interested in using a data blob
+and would like the System to not delete this data blob. Applications can also access a blob
+without holding a lease on it, in which case the System can choose to delete the data blob at any
+time. So, if an application wants to make sure a data blob is available for access for a certain
+period, it is recommended that the application acquire a lease on the data blob. Applications can
+either specify upfront how long they would like to hold the lease for (which is called the lease
+expiry time), or they can acquire a lease without specifying a time period and release the lease
+when they are done with the data blob.
+
+## Sharing data blobs across users
+By default, data blobs are only accessible to applications in the user in which the data blob was
+contributed, but if an application holds the permission
+[ACCESS_BLOBS_ACROSS_USERS](https://developer.android.com/reference/android/Manifest.permission#ACCESS_BLOBS_ACROSS_USERS),
+then they are allowed to access blobs that are contributed by the applications in the other users.
+As of Android S, this permission is only available to following set of applications:
+* Apps signed with the platform certificate
+* Privileged applications
+* Applications holding the
+  [ASSISTANT](https://developer.android.com/reference/android/app/role/RoleManager#ROLE_ASSISTANT)
+  role
+* Development applications
+
+Note that the access modes that applications choose while committing the data blobs still apply
+when these data blobs are accessed across users. So for example, if *appA* contributed a
+data blob in *user0* and specified to share this data blob with only a specific set of
+applications [*appB*, *appC*], then *appD* on *user10* will not be able to access this data blob
+even if the app is granted the `ACCESS_BLOBS_ACROSS_USERS` permission.
+
+When apps that are allowed to access blobs across users
+(i.e. those holding the permission `ACCESS_BLOBS_ACROSS_USERS`) try to access a data blob,
+they can do so as if it is any other data blob. In other words, the applications don’t need to
+know where the data blob is contributed, because the System will automatically check and will
+allow access if this data blob is available either on the user in which the calling application
+is running in or other users.
\ No newline at end of file
diff --git a/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java b/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java
index 316306d..5ab4207 100644
--- a/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java
+++ b/apex/jobscheduler/service/java/com/android/server/AnyMotionDetector.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import android.annotation.Nullable;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -26,6 +27,8 @@
 import android.os.SystemClock;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
+
 /**
  * Determines if the device has been set upon a stationary object.
  */
@@ -54,6 +57,7 @@
     private static final int STATE_ACTIVE = 1;
 
     /** Current measurement state. */
+    @GuardedBy("mLock")
     private int mState;
 
     /** Threshold energy above which the device is considered moving. */
@@ -82,38 +86,47 @@
 
     private final Handler mHandler;
     private final Object mLock = new Object();
-    private Sensor mAccelSensor;
-    private SensorManager mSensorManager;
-    private PowerManager.WakeLock mWakeLock;
+    private final Sensor mAccelSensor;
+    private final SensorManager mSensorManager;
+    private final PowerManager.WakeLock mWakeLock;
 
     /** Threshold angle in degrees beyond which the device is considered moving. */
     private final float mThresholdAngle;
 
     /** The minimum number of samples required to detect AnyMotion. */
+    @GuardedBy("mLock")
     private int mNumSufficientSamples;
 
     /** True if an orientation measurement is in progress. */
+    @GuardedBy("mLock")
     private boolean mMeasurementInProgress;
 
     /** True if sendMessageDelayed() for the mMeasurementTimeout callback has been scheduled */
+    @GuardedBy("mLock")
     private boolean mMeasurementTimeoutIsActive;
 
     /** True if sendMessageDelayed() for the mWakelockTimeout callback has been scheduled */
-    private boolean mWakelockTimeoutIsActive;
+    private volatile boolean mWakelockTimeoutIsActive;
 
     /** True if sendMessageDelayed() for the mSensorRestart callback has been scheduled */
+    @GuardedBy("mLock")
     private boolean mSensorRestartIsActive;
 
     /** The most recent gravity vector. */
+    @Nullable
+    @GuardedBy("mLock")
     private Vector3 mCurrentGravityVector = null;
 
     /** The second most recent gravity vector. */
+    @Nullable
+    @GuardedBy("mLock")
     private Vector3 mPreviousGravityVector = null;
 
     /** Running sum of squared errors. */
-    private RunningSignalStats mRunningStats;
+    @GuardedBy("mLock")
+    private final RunningSignalStats mRunningStats;
 
-    private DeviceIdleCallback mCallback = null;
+    private final DeviceIdleCallback mCallback;
 
     public AnyMotionDetector(PowerManager pm, Handler handler, SensorManager sm,
             DeviceIdleCallback callback, float thresholdAngle) {
@@ -149,11 +162,11 @@
      * Acquire accel data until we determine AnyMotion status.
      */
     public void checkForAnyMotion() {
-        if (DEBUG) {
-            Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
-        }
-        if (mState != STATE_ACTIVE) {
-            synchronized (mLock) {
+        synchronized (mLock) {
+            if (DEBUG) {
+                Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+            }
+            if (mState != STATE_ACTIVE) {
                 mState = STATE_ACTIVE;
                 if (DEBUG) {
                     Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
@@ -193,6 +206,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private void startOrientationMeasurementLocked() {
         if (DEBUG) Slog.d(TAG, "startOrientationMeasurementLocked: mMeasurementInProgress=" +
             mMeasurementInProgress + ", (mAccelSensor != null)=" + (mAccelSensor != null));
@@ -208,6 +222,7 @@
         }
     }
 
+    @GuardedBy("mLock")
     private int stopOrientationMeasurementLocked() {
         if (DEBUG) Slog.d(TAG, "stopOrientationMeasurement. mMeasurementInProgress=" +
                 mMeasurementInProgress);
@@ -231,7 +246,7 @@
                 Slog.d(TAG, "mCurrentGravityVector = " + currentGravityVectorString);
                 Slog.d(TAG, "mPreviousGravityVector = " + previousGravityVectorString);
             }
-            status = getStationaryStatus();
+            status = getStationaryStatusLocked();
             mRunningStats.reset();
             if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
             if (status != RESULT_UNKNOWN) {
@@ -261,9 +276,10 @@
     }
 
     /*
-     * Updates mStatus to the current AnyMotion status.
+     * Returns the current AnyMotion status.
      */
-    public int getStationaryStatus() {
+    @GuardedBy("mLock")
+    private int getStationaryStatusLocked() {
         if ((mPreviousGravityVector == null) || (mCurrentGravityVector == null)) {
             return RESULT_UNKNOWN;
         }
@@ -341,13 +357,13 @@
                           "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
                           "orientation measurement.");
                     status = stopOrientationMeasurementLocked();
-                    if (status != RESULT_UNKNOWN) {
-                        mHandler.removeCallbacks(mWakelockTimeout);
-                        mWakelockTimeoutIsActive = false;
-                        mCallback.onAnyMotionResult(status);
-                    }
                 }
             }
+            if (status != RESULT_UNKNOWN) {
+                mHandler.removeCallbacks(mWakelockTimeout);
+                mWakelockTimeoutIsActive = false;
+                mCallback.onAnyMotionResult(status);
+            }
         }
     };
 
@@ -488,9 +504,10 @@
             }
         }
 
+        @Nullable
         public Vector3 getRunningAverage() {
             if (sampleCount > 0) {
-              return runningSum.times((float)(1.0f / sampleCount));
+                return runningSum.times(1.0f / sampleCount);
             }
             return null;
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 19aed49..0116364 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -298,27 +298,45 @@
     private Intent mLightIdleIntent;
     private AnyMotionDetector mAnyMotionDetector;
     private final AppStateTrackerImpl mAppStateTracker;
+    @GuardedBy("this")
     private boolean mLightEnabled;
+    @GuardedBy("this")
     private boolean mDeepEnabled;
+    @GuardedBy("this")
     private boolean mQuickDozeActivated;
+    @GuardedBy("this")
     private boolean mQuickDozeActivatedWhileIdling;
+    @GuardedBy("this")
     private boolean mForceIdle;
+    @GuardedBy("this")
     private boolean mNetworkConnected;
+    @GuardedBy("this")
     private boolean mScreenOn;
+    @GuardedBy("this")
     private boolean mCharging;
+    @GuardedBy("this")
     private boolean mNotMoving;
+    @GuardedBy("this")
     private boolean mLocating;
+    @GuardedBy("this")
     private boolean mLocated;
+    @GuardedBy("this")
     private boolean mHasGps;
+    @GuardedBy("this")
     private boolean mHasNetworkLocation;
+    @GuardedBy("this")
     private Location mLastGenericLocation;
+    @GuardedBy("this")
     private Location mLastGpsLocation;
 
     /** Time in the elapsed realtime timebase when this listener last received a motion event. */
+    @GuardedBy("this")
     private long mLastMotionEventElapsed;
 
     // Current locked state of the screen
+    @GuardedBy("this")
     private boolean mScreenLocked;
+    @GuardedBy("this")
     private int mNumBlockingConstraints = 0;
 
     /**
@@ -434,19 +452,30 @@
         }
     }
 
+    @GuardedBy("this")
     private int mState;
+    @GuardedBy("this")
     private int mLightState;
 
+    @GuardedBy("this")
     private long mInactiveTimeout;
+    @GuardedBy("this")
     private long mNextAlarmTime;
+    @GuardedBy("this")
     private long mNextIdlePendingDelay;
+    @GuardedBy("this")
     private long mNextIdleDelay;
+    @GuardedBy("this")
     private long mNextLightIdleDelay;
+    @GuardedBy("this")
     private long mNextLightIdleDelayFlex;
+    @GuardedBy("this")
     private long mNextLightAlarmTime;
+    @GuardedBy("this")
     private long mNextSensingTimeoutAlarmTime;
 
     /** How long a light idle maintenance window should last. */
+    @GuardedBy("this")
     private long mCurLightIdleBudget;
 
     /**
@@ -454,15 +483,20 @@
      * only if {@link #mState} == {@link #STATE_IDLE_MAINTENANCE} or
      * {@link #mLightState} == {@link #LIGHT_STATE_IDLE_MAINTENANCE}.
      */
+    @GuardedBy("this")
     private long mMaintenanceStartTime;
+    @GuardedBy("this")
     private long mIdleStartTime;
 
+    @GuardedBy("this")
     private int mActiveIdleOpCount;
     private PowerManager.WakeLock mActiveIdleWakeLock; // held when there are operations in progress
     private PowerManager.WakeLock mGoingIdleWakeLock;  // held when we are going idle so hardware
                                                        // (especially NetworkPolicyManager) can shut
                                                        // down.
+    @GuardedBy("this")
     private boolean mJobsActive;
+    @GuardedBy("this")
     private boolean mAlarmsActive;
 
     /* Factor to apply to INACTIVE_TIMEOUT and IDLE_AFTER_INACTIVE_TIMEOUT in order to enter
@@ -472,8 +506,11 @@
      * Also don't apply the factor if the device is in motion because device motion provides a
      * stronger signal than a prediction algorithm.
      */
+    @GuardedBy("this")
     private float mPreIdleFactor;
+    @GuardedBy("this")
     private float mLastPreIdleFactor;
+    @GuardedBy("this")
     private int mActiveReason;
 
     public final AtomicFile mConfigFile;
@@ -659,8 +696,8 @@
             = new AlarmManager.OnAlarmListener() {
         @Override
         public void onAlarm() {
-            if (mState == STATE_SENSING) {
-                synchronized (DeviceIdleController.this) {
+            synchronized (DeviceIdleController.this) {
+                if (mState == STATE_SENSING) {
                     // Restart the device idle progression in case the device moved but the screen
                     // didn't turn on.
                     becomeInactiveIfAppropriateLocked();
@@ -714,6 +751,7 @@
         mHandler.sendEmptyMessage(MSG_REPORT_STATIONARY_STATUS);
     }
 
+    @GuardedBy("this")
     private boolean isStationaryLocked() {
         final long now = mInjector.getElapsedRealtime();
         return mMotionListener.active
@@ -1592,27 +1630,21 @@
     @Override
     public void onAnyMotionResult(int result) {
         if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
-        if (result != AnyMotionDetector.RESULT_UNKNOWN) {
-            synchronized (this) {
+        synchronized (this) {
+            if (result != AnyMotionDetector.RESULT_UNKNOWN) {
                 cancelSensingTimeoutAlarmLocked();
             }
-        }
-        if ((result == AnyMotionDetector.RESULT_MOVED) ||
-            (result == AnyMotionDetector.RESULT_UNKNOWN)) {
-            synchronized (this) {
+            if ((result == AnyMotionDetector.RESULT_MOVED)
+                    || (result == AnyMotionDetector.RESULT_UNKNOWN)) {
                 handleMotionDetectedLocked(mConstants.INACTIVE_TIMEOUT, "non_stationary");
-            }
-        } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
-            if (mState == STATE_SENSING) {
-                // If we are currently sensing, it is time to move to locating.
-                synchronized (this) {
+            } else if (result == AnyMotionDetector.RESULT_STATIONARY) {
+                if (mState == STATE_SENSING) {
+                    // If we are currently sensing, it is time to move to locating.
                     mNotMoving = true;
                     stepIdleStateLocked("s:stationary");
-                }
-            } else if (mState == STATE_LOCATING) {
-                // If we are currently locating, note that we are not moving and step
-                // if we have located the position.
-                synchronized (this) {
+                } else if (mState == STATE_LOCATING) {
+                    // If we are currently locating, note that we are not moving and step
+                    // if we have located the position.
                     mNotMoving = true;
                     if (mLocated) {
                         stepIdleStateLocked("s:stationary");
@@ -3062,6 +3094,7 @@
         }
     }
 
+    @GuardedBy("this")
     void updateInteractivityLocked() {
         // The interactivity state from the power manager tells us whether the display is
         // in a state that we need to keep things running so they will update at a normal
@@ -3089,6 +3122,7 @@
         }
     }
 
+    @GuardedBy("this")
     void updateChargingLocked(boolean charging) {
         if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
         if (!charging && mCharging) {
@@ -3114,6 +3148,7 @@
 
     /** Updates the quick doze flag and enters deep doze if appropriate. */
     @VisibleForTesting
+    @GuardedBy("this")
     void updateQuickDozeFlagLocked(boolean enabled) {
         if (DEBUG) Slog.i(TAG, "updateQuickDozeFlagLocked: enabled=" + enabled);
         mQuickDozeActivated = enabled;
@@ -3138,6 +3173,7 @@
     }
 
     @VisibleForTesting
+    @GuardedBy("this")
     void keyguardShowingLocked(boolean showing) {
         if (DEBUG) Slog.i(TAG, "keyguardShowing=" + showing);
         if (mScreenLocked != showing) {
@@ -3150,15 +3186,18 @@
     }
 
     @VisibleForTesting
+    @GuardedBy("this")
     void scheduleReportActiveLocked(String activeReason, int activeUid) {
         Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid, 0, activeReason);
         mHandler.sendMessage(msg);
     }
 
+    @GuardedBy("this")
     void becomeActiveLocked(String activeReason, int activeUid) {
         becomeActiveLocked(activeReason, activeUid, mConstants.INACTIVE_TIMEOUT, true);
     }
 
+    @GuardedBy("this")
     private void becomeActiveLocked(String activeReason, int activeUid,
             long newInactiveTimeout, boolean changeLightIdle) {
         if (DEBUG) {
@@ -3204,6 +3243,7 @@
     }
 
     /** Sanity check to make sure DeviceIdleController and AlarmManager are on the same page. */
+    @GuardedBy("this")
     private void verifyAlarmStateLocked() {
         if (mState == STATE_ACTIVE && mNextAlarmTime != 0) {
             Slog.wtf(TAG, "mState=ACTIVE but mNextAlarmTime=" + mNextAlarmTime);
@@ -3221,6 +3261,7 @@
         }
     }
 
+    @GuardedBy("this")
     void becomeInactiveIfAppropriateLocked() {
         verifyAlarmStateLocked();
 
@@ -3300,6 +3341,7 @@
         }
     }
 
+    @GuardedBy("this")
     private void resetIdleManagementLocked() {
         mNextIdlePendingDelay = 0;
         mNextIdleDelay = 0;
@@ -3313,6 +3355,7 @@
         updateActiveConstraintsLocked();
     }
 
+    @GuardedBy("this")
     private void resetLightIdleManagementLocked() {
         mNextLightIdleDelay = 0;
         mNextLightIdleDelayFlex = 0;
@@ -3320,6 +3363,7 @@
         cancelLightAlarmLocked();
     }
 
+    @GuardedBy("this")
     void exitForceIdleLocked() {
         if (mForceIdle) {
             mForceIdle = false;
@@ -3344,9 +3388,12 @@
 
     @VisibleForTesting
     int getLightState() {
-        return mLightState;
+        synchronized (this) {
+            return mLightState;
+        }
     }
 
+    @GuardedBy("this")
     void stepLightIdleStateLocked(String reason) {
         if (mLightState == LIGHT_STATE_OVERRIDE) {
             // If we are already in deep device idle mode, then
@@ -3435,7 +3482,9 @@
 
     @VisibleForTesting
     int getState() {
-        return mState;
+        synchronized (this) {
+            return mState;
+        }
     }
 
     /**
@@ -3448,6 +3497,7 @@
     }
 
     @VisibleForTesting
+    @GuardedBy("this")
     void stepIdleStateLocked(String reason) {
         if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
         EventLogTags.writeDeviceIdleStep();
@@ -3588,6 +3638,7 @@
         }
     }
 
+    @GuardedBy("this")
     private void moveToStateLocked(int state, String reason) {
         final int oldState = mState;
         mState = state;
@@ -3667,22 +3718,26 @@
 
     @VisibleForTesting
     float getPreIdleTimeoutFactor() {
-        return mPreIdleFactor;
+        synchronized (this) {
+            return mPreIdleFactor;
+        }
     }
 
     @VisibleForTesting
     int setPreIdleTimeoutFactor(float ratio) {
-        if (!mDeepEnabled) {
-            if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Deep Idle disable");
-            return SET_IDLE_FACTOR_RESULT_NOT_SUPPORT;
-        } else if (ratio <= MIN_PRE_IDLE_FACTOR_CHANGE) {
-            if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Invalid input");
-            return SET_IDLE_FACTOR_RESULT_INVALID;
-        } else if (Math.abs(ratio - mPreIdleFactor) < MIN_PRE_IDLE_FACTOR_CHANGE) {
-            if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: New factor same as previous factor");
-            return SET_IDLE_FACTOR_RESULT_IGNORED;
-        }
         synchronized (this) {
+            if (!mDeepEnabled) {
+                if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Deep Idle disable");
+                return SET_IDLE_FACTOR_RESULT_NOT_SUPPORT;
+            } else if (ratio <= MIN_PRE_IDLE_FACTOR_CHANGE) {
+                if (DEBUG) Slog.d(TAG, "setPreIdleTimeoutFactor: Invalid input");
+                return SET_IDLE_FACTOR_RESULT_INVALID;
+            } else if (Math.abs(ratio - mPreIdleFactor) < MIN_PRE_IDLE_FACTOR_CHANGE) {
+                if (DEBUG) {
+                    Slog.d(TAG, "setPreIdleTimeoutFactor: New factor same as previous factor");
+                }
+                return SET_IDLE_FACTOR_RESULT_IGNORED;
+            }
             mLastPreIdleFactor = mPreIdleFactor;
             mPreIdleFactor = ratio;
         }
@@ -3744,6 +3799,7 @@
         }
     }
 
+    @GuardedBy("this")
     private boolean shouldUseIdleTimeoutFactorLocked() {
         // exclude ACTIVE_REASON_MOTION, for exclude device in pocket case
         if (mActiveReason == ACTIVE_REASON_MOTION) {
@@ -3763,13 +3819,17 @@
 
     @VisibleForTesting
     long getNextAlarmTime() {
-        return mNextAlarmTime;
+        synchronized (this) {
+            return mNextAlarmTime;
+        }
     }
 
+    @GuardedBy("this")
     boolean isOpsInactiveLocked() {
         return mActiveIdleOpCount <= 0 && !mJobsActive && !mAlarmsActive;
     }
 
+    @GuardedBy("this")
     void exitMaintenanceEarlyIfNeededLocked() {
         if (mState == STATE_IDLE_MAINTENANCE || mLightState == LIGHT_STATE_IDLE_MAINTENANCE
                 || mLightState == LIGHT_STATE_PRE_IDLE) {
@@ -3794,12 +3854,14 @@
         }
     }
 
+    @GuardedBy("this")
     void motionLocked() {
         if (DEBUG) Slog.d(TAG, "motionLocked()");
         mLastMotionEventElapsed = mInjector.getElapsedRealtime();
         handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
     }
 
+    @GuardedBy("this")
     void handleMotionDetectedLocked(long timeout, String type) {
         if (mStationaryListeners.size() > 0) {
             postStationaryStatusUpdated();
@@ -3829,6 +3891,7 @@
         }
     }
 
+    @GuardedBy("this")
     void receivedGenericLocationLocked(Location location) {
         if (mState != STATE_LOCATING) {
             cancelLocatingLocked();
@@ -3845,6 +3908,7 @@
         }
     }
 
+    @GuardedBy("this")
     void receivedGpsLocationLocked(Location location) {
         if (mState != STATE_LOCATING) {
             cancelLocatingLocked();
@@ -3883,6 +3947,7 @@
         }
     }
 
+    @GuardedBy("this")
     void cancelAlarmLocked() {
         if (mNextAlarmTime != 0) {
             mNextAlarmTime = 0;
@@ -3890,6 +3955,7 @@
         }
     }
 
+    @GuardedBy("this")
     void cancelLightAlarmLocked() {
         if (mNextLightAlarmTime != 0) {
             mNextLightAlarmTime = 0;
@@ -3897,6 +3963,7 @@
         }
     }
 
+    @GuardedBy("this")
     void cancelLocatingLocked() {
         if (mLocating) {
             LocationManager locationManager = mInjector.getLocationManager();
@@ -3914,6 +3981,7 @@
         mAlarmManager.cancel(mMotionRegistrationAlarmListener);
     }
 
+    @GuardedBy("this")
     void cancelSensingTimeoutAlarmLocked() {
         if (mNextSensingTimeoutAlarmTime != 0) {
             mNextSensingTimeoutAlarmTime = 0;
@@ -3921,6 +3989,7 @@
         }
     }
 
+    @GuardedBy("this")
     void scheduleAlarmLocked(long delay, boolean idleUntil) {
         if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
 
@@ -3957,6 +4026,7 @@
         }
     }
 
+    @GuardedBy("this")
     void scheduleLightAlarmLocked(long delay, long flex) {
         if (DEBUG) {
             Slog.d(TAG, "scheduleLightAlarmLocked(" + delay
@@ -4003,6 +4073,7 @@
         }
     }
 
+    @GuardedBy("this")
     void scheduleSensingTimeoutAlarmLocked(long delay) {
         if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
         mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
@@ -4071,6 +4142,7 @@
      * @param callingUid the callingUid that setup this temp-allowlist, only valid when param adding
      *                   is true.
      */
+    @GuardedBy("this")
     private void updateTempWhitelistAppIdsLocked(int uid, boolean adding, long durationMs,
             @TempAllowListType int type, @ReasonCode int reasonCode, @Nullable String reason,
             int callingUid) {
@@ -4120,6 +4192,7 @@
                 mTempWhitelistAppIdArray);
     }
 
+    @GuardedBy("this")
     void readConfigFileLocked() {
         if (DEBUG) Slog.d(TAG, "Reading config from " + mConfigFile.getBaseFile());
         mPowerSaveWhitelistUserApps.clear();
@@ -4142,6 +4215,7 @@
         }
     }
 
+    @GuardedBy("this")
     private void readConfigFileLocked(XmlPullParser parser) {
         final PackageManager pm = getContext().getPackageManager();
 
@@ -4987,7 +5061,7 @@
                 pw.println();
             }
             if (mNextLightIdleDelay != 0) {
-                pw.print("  mNextIdleDelay=");
+                pw.print("  mNextLightIdleDelay=");
                 TimeUtils.formatDuration(mNextLightIdleDelay, pw);
                 if (mConstants.USE_WINDOW_ALARMS) {
                     pw.print(" (flex=");
diff --git a/apex/media/framework/java/android/media/MediaCommunicationManager.java b/apex/media/framework/java/android/media/MediaCommunicationManager.java
index b2528cd..0f00dbb 100644
--- a/apex/media/framework/java/android/media/MediaCommunicationManager.java
+++ b/apex/media/framework/java/android/media/MediaCommunicationManager.java
@@ -74,6 +74,9 @@
     @GuardedBy("mLock")
     private MediaCommunicationServiceCallbackStub mCallbackStub;
 
+    // TODO: remove this when MCS implements dispatchMediaKeyEvent.
+    private MediaSessionManager mMediaSessionManager;
+
     /**
      * @hide
      */
@@ -222,6 +225,14 @@
         return mService;
     }
 
+    // TODO: remove this when MCS implements dispatchMediaKeyEvent.
+    private MediaSessionManager getMediaSessionManager() {
+        if (mMediaSessionManager == null) {
+            mMediaSessionManager = mContext.getSystemService(MediaSessionManager.class);
+        }
+        return mMediaSessionManager;
+    }
+
     private List<Session2Token> getSession2Tokens(int userId) {
         try {
             MediaParceledListSlice slice = getService().getSession2Tokens(userId);
@@ -243,6 +254,14 @@
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean asSystemService) {
         Objects.requireNonNull(keyEvent, "keyEvent shouldn't be null");
+
+        // When MCS handles this, caller is changed.
+        // TODO: remove this when MCS implementation is done.
+        if (!asSystemService) {
+            getMediaSessionManager().dispatchMediaKeyEvent(keyEvent, false);
+            return;
+        }
+
         try {
             getService().dispatchMediaKeyEvent(mContext.getPackageName(),
                     keyEvent, asSystemService);
diff --git a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
index 03a2372..09f65b5 100644
--- a/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
+++ b/apex/media/service/java/com/android/server/media/MediaCommunicationService.java
@@ -395,11 +395,7 @@
             final long token = Binder.clearCallingIdentity();
             try {
                 //TODO: Dispatch key event to media session 2 if required
-                if (asSystemService) {
-                    mSessionManager.dispatchMediaKeyEventAsSystemService(keyEvent);
-                } else {
-                    mSessionManager.dispatchMediaKeyEvent(keyEvent, false);
-                }
+                mSessionManager.dispatchMediaKeyEvent(keyEvent, asSystemService);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b8ec6fc..21ba183 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -425,6 +425,7 @@
 
   public class DevicePolicyManager {
     method public int checkProvisioningPreCondition(@Nullable String, @NonNull String);
+    method @RequiresPermission("android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS") public void clearOrganizationId();
     method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
     method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
     method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7102314..7a043a8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -13754,6 +13754,23 @@
     }
 
     /**
+     * Clears organization ID set by the DPC and resets the precomputed enrollment specific ID.
+     * @hide
+     */
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+    public void clearOrganizationId() {
+        if (mService == null) {
+            return;
+        }
+        try {
+            mService.clearOrganizationIdForUser(myUserId());
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates and provisions a managed profile and sets the
      * {@link ManagedProfileProvisioningParams#getProfileAdminComponentName()} as the profile
      * owner.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 4e4c2219..fc49f79 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -375,6 +375,7 @@
 
     void setOrganizationColor(in ComponentName admin, in int color);
     void setOrganizationColorForUser(in int color, in int userId);
+    void clearOrganizationIdForUser(int userHandle);
     int getOrganizationColor(in ComponentName admin);
     int getOrganizationColorForUser(int userHandle);
 
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 865940e..01e237d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -153,7 +153,8 @@
             int index, byte[] value);
     private static native void nativeResetStatementAndClearBindings(
             long connectionPtr, long statementPtr);
-    private static native void nativeExecute(long connectionPtr, long statementPtr);
+    private static native void nativeExecute(long connectionPtr, long statementPtr,
+            boolean isPragmaStmt);
     private static native long nativeExecuteForLong(long connectionPtr, long statementPtr);
     private static native String nativeExecuteForString(long connectionPtr, long statementPtr);
     private static native int nativeExecuteForBlobFileDescriptor(
@@ -699,6 +700,8 @@
 
         final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs);
         try {
+            final boolean isPragmaStmt =
+                DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_PRAGMA;
             final PreparedStatement statement = acquirePreparedStatement(sql);
             try {
                 throwIfStatementForbidden(statement);
@@ -706,7 +709,7 @@
                 applyBlockGuardPolicy(statement);
                 attachCancellationSignal(cancellationSignal);
                 try {
-                    nativeExecute(mConnectionPtr, statement.mStatementPtr);
+                    nativeExecute(mConnectionPtr, statement.mStatementPtr, isPragmaStmt);
                 } finally {
                     detachCancellationSignal(cancellationSignal);
                 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 7c42dc0..d48d562 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -850,26 +850,6 @@
     }
 
     /**
-     * Checks if the specified user has enrollments in any of the specified sensors.
-     * @hide
-     */
-    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
-    public boolean hasEnrolledTemplatesForAnySensor(int userId,
-            @NonNull List<FingerprintSensorPropertiesInternal> sensors) {
-        if (mService == null) {
-            Slog.w(TAG, "hasEnrolledTemplatesForAnySensor: no fingerprint service");
-            return false;
-        }
-
-        try {
-            return mService.hasEnrolledTemplatesForAnySensor(userId, sensors,
-                    mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * @hide
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
diff --git a/core/java/android/hardware/fingerprint/FingerprintStateListener.java b/core/java/android/hardware/fingerprint/FingerprintStateListener.java
index 6e607a2..cf914c5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintStateListener.java
+++ b/core/java/android/hardware/fingerprint/FingerprintStateListener.java
@@ -49,5 +49,10 @@
      * Defines behavior in response to state update
      * @param newState new state of fingerprint sensor
      */
-    public abstract void onStateChanged(@FingerprintStateListener.State int newState);
+    public void onStateChanged(@FingerprintStateListener.State int newState) {};
+
+    /**
+     * Invoked when enrollment state changes for the specified user
+     */
+    public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {};
 }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 4774827..de94b2f 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -120,9 +120,6 @@
     // Determine if a user has at least one enrolled fingerprint.
     boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName);
 
-    // Determine if a user has at least one enrolled fingerprint in any of the specified sensors
-    boolean hasEnrolledTemplatesForAnySensor(int userId, in List<FingerprintSensorPropertiesInternal> sensors, String opPackageName);
-
     // Return the LockoutTracker status for the specified user
     int getLockoutModeForUser(int sensorId, int userId);
 
diff --git a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl
index 56dba7e..1aa6fa1 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintStateListener.aidl
@@ -24,4 +24,5 @@
  */
 oneway interface IFingerprintStateListener {
     void onStateChanged(int newState);
+    void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments);
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f132ac5..2bb0ec7 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -304,6 +304,8 @@
 
     @CriticalNative
     private static native void nativeMarkSensitive(long nativePtr);
+    @FastNative
+    private static native void nativeMarkForBinder(long nativePtr, IBinder binder);
     @CriticalNative
     private static native int nativeDataSize(long nativePtr);
     @CriticalNative
@@ -528,6 +530,16 @@
 
     /**
      * Parcel data should be zero'd before realloc'd or deleted.
+     *
+     * Note: currently this feature requires multiple things to work in concert:
+     * - markSensitive must be called on every relative Parcel
+     * - FLAG_CLEAR_BUF must be passed into the kernel
+     * This requires having code which does the right thing in every method and in every backend
+     * of AIDL. Rather than exposing this API, it should be replaced with a single API on
+     * IBinder objects which can be called once, and the information should be fed into the
+     * Parcel using markForBinder APIs. In terms of code size and number of API calls, this is
+     * much more extensible.
+     *
      * @hide
      */
     public final void markSensitive() {
@@ -535,9 +547,23 @@
     }
 
     /**
+     * Associate this parcel with a binder object. This marks the parcel as being prepared for a
+     * transaction on this specific binder object. Based on this, the format of the wire binder
+     * protocol may change. This should be called before any data is written to the parcel. If this
+     * is called multiple times, this will only be marked for the last binder. For future
+     * compatibility, it is recommended to call this on all parcels which are being sent over
+     * binder.
+     *
+     * @hide
+     */
+    public void markForBinder(@NonNull IBinder binder) {
+        nativeMarkForBinder(mNativePtr, binder);
+    }
+
+    /**
      * Returns the total amount of data contained in the parcel.
      */
-    public final int dataSize() {
+    public int dataSize() {
         return nativeDataSize(mNativePtr);
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9e2b6e0..0dd6e22 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4031,6 +4031,10 @@
                 mBlastBufferQueue.setNextTransaction(null);
                 mBlastBufferQueue.setTransactionCompleteCallback(mRtLastAttemptedDrawFrameNum,
                         null);
+                // Apply the transactions that were sent to mergeWithNextTransaction since the
+                // frame didn't draw on this vsync. It's possible the frame will draw later, but
+                // it's better to not be sync than to block on a frame that may never come.
+                mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
             }
 
             mHandler.postAtFrontOfQueue(() -> {
@@ -4086,18 +4090,16 @@
 
     private void addFrameCallbackIfNeeded() {
         final boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
-        final boolean reportNextDraw = mReportNextDraw;
         final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
         final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
 
-        if (!nextDrawUseBlastSync && !reportNextDraw && !needsCallbackForBlur) {
+        if (!nextDrawUseBlastSync && !needsCallbackForBlur) {
             return;
         }
 
         if (DEBUG_BLAST) {
             Log.d(mTag, "Creating frameDrawingCallback"
                     + " nextDrawUseBlastSync=" + nextDrawUseBlastSync
-                    + " reportNextDraw=" + reportNextDraw
                     + " hasBlurUpdates=" + hasBlurUpdates);
         }
         mWaitForBlastSyncComplete = nextDrawUseBlastSync;
@@ -4137,11 +4139,6 @@
                     }
                     mHandler.postAtFrontOfQueue(this::clearBlastSync);
                 });
-            } else if (reportNextDraw) {
-                // If we need to report next draw, wait for adapter to flush its shadow queue
-                // by processing previously queued buffers so that we can submit the
-                // transaction a timely manner.
-                mBlastBufferQueue.flushShadowQueue();
             }
         };
         registerRtFrameCallback(frameDrawingCallback);
diff --git a/core/java/android/view/translation/Helper.java b/core/java/android/view/translation/Helper.java
new file mode 100644
index 0000000..6e2850f
--- /dev/null
+++ b/core/java/android/view/translation/Helper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 android.view.translation;
+
+/** @hide */
+public class Helper {
+
+    // Debug-level flags are defined when service is bound.
+    public static boolean sDebug = false;
+    public static boolean sVerbose = false;
+
+    // TODO: Use a device config value.
+    public static final int ANIMATION_DURATION_MILLIS = 250;
+
+    private Helper() {
+        throw new UnsupportedOperationException("contains static members only");
+    }
+}
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 606f39d..70db6e5 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -53,9 +53,6 @@
 
     private static final String TAG = "Translator";
 
-    // TODO: make this configurable and cross the Translation component
-    private static boolean sDEBUG = false;
-
     private final Object mLock = new Object();
 
     private int mId;
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 442d099..1019612 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -16,6 +16,7 @@
 
 package android.view.translation;
 
+import static android.view.translation.Helper.ANIMATION_DURATION_MILLIS;
 import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED;
 import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED;
 import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED;
@@ -26,6 +27,7 @@
 import android.app.Activity;
 import android.app.assist.ActivityId;
 import android.content.Context;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
@@ -465,9 +467,6 @@
         }
     }
 
-    // TODO: Use a device config value.
-    private static final int ANIMATION_DURATION_MILLIS = 250;
-
     /**
      * Creates a Translator for the given source and target translation specs and start the ui
      * translation when the Translator is created successfully.
@@ -724,7 +723,21 @@
             msg.append("text=").append(value.getText() == null
                     ? "null"
                     : "string[" + value.getText().length() + "], ");
-            //TODO: append dictionary results.
+            final Bundle definitions =
+                    (Bundle) value.getExtras().get(TranslationResponseValue.EXTRA_DEFINITIONS);
+            if (definitions != null) {
+                msg.append("definitions={");
+                for (String partOfSpeech : definitions.keySet()) {
+                    msg.append(partOfSpeech).append(":[");
+                    for (CharSequence definition : definitions.getCharSequenceArray(partOfSpeech)) {
+                        msg.append(definition == null
+                                ? "null, "
+                                : "string[" + definition.length() + "], ");
+                    }
+                    msg.append("], ");
+                }
+                msg.append("}");
+            }
             msg.append("transliteration=").append(value.getTransliteration() == null
                     ? "null"
                     : "string[" + value.getTransliteration().length() + "]}, ");
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 4d0f4c2..019126f 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -140,6 +140,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 
 /**
  * The Chooser Activity handles intent resolution specifically for sharing intents -
@@ -234,11 +235,6 @@
 
     private static final float DIRECT_SHARE_EXPANSION_RATE = 0.78f;
 
-    // TODO(b/121287224): Re-evaluate this limit
-    private static final int SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
-
-    private static final int QUERY_TARGET_SERVICE_LIMIT = 5;
-
     private static final int DEFAULT_SALT_EXPIRATION_DAYS = 7;
     private int mMaxHashSaltDays = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
             SystemUiDeviceConfigFlags.HASH_SALT_MAX_DAYS,
@@ -258,8 +254,6 @@
 
     private long mQueriedSharingShortcutsTimeMs;
 
-    private int mChooserRowServiceSpacing;
-
     private int mCurrAvailableWidth = 0;
     private int mLastNumberOfChildren = -1;
 
@@ -658,9 +652,6 @@
                 .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
                 .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
 
-        mChooserRowServiceSpacing = getResources()
-                                        .getDimensionPixelSize(R.dimen.chooser_service_spacing);
-
         if (mResolverDrawerLayout != null) {
             mResolverDrawerLayout.addOnLayoutChangeListener(this::handleLayoutChange);
 
@@ -2066,24 +2057,6 @@
         return chooserTargetList;
     }
 
-    private String convertServiceName(String packageName, String serviceName) {
-        if (TextUtils.isEmpty(serviceName)) {
-            return null;
-        }
-
-        final String fullName;
-        if (serviceName.startsWith(".")) {
-            // Relative to the app package. Prepend the app package name.
-            fullName = packageName + serviceName;
-        } else if (serviceName.indexOf('.') >= 0) {
-            // Fully qualified package name.
-            fullName = serviceName;
-        } else {
-            fullName = null;
-        }
-        return fullName;
-    }
-
     private void logDirectShareTargetReceived(int logCategory) {
         final int apiLatency = (int) (System.currentTimeMillis() - mQueriedSharingShortcutsTimeMs);
         getMetricsLogger().write(new LogMaker(logCategory).setSubtype(apiLatency));
@@ -2265,37 +2238,6 @@
         return false;
     }
 
-    void filterServiceTargets(Context contextAsUser, String packageName,
-            List<ChooserTarget> targets) {
-        if (targets == null) {
-            return;
-        }
-
-        final PackageManager pm = contextAsUser.getPackageManager();
-        for (int i = targets.size() - 1; i >= 0; i--) {
-            final ChooserTarget target = targets.get(i);
-            final ComponentName targetName = target.getComponentName();
-            if (packageName != null && packageName.equals(targetName.getPackageName())) {
-                // Anything from the original target's package is fine.
-                continue;
-            }
-
-            boolean remove;
-            try {
-                final ActivityInfo ai = pm.getActivityInfo(targetName, 0);
-                remove = !ai.exported || ai.permission != null;
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "Target " + target + " returned by " + packageName
-                        + " component not found");
-                remove = true;
-            }
-
-            if (remove) {
-                targets.remove(i);
-            }
-        }
-    }
-
     /**
      * Sort intents alphabetically based on display label.
      */
@@ -2854,7 +2796,7 @@
                 .setSubtype(previewType));
     }
 
-    class ViewHolderBase extends RecyclerView.ViewHolder {
+    abstract static class ViewHolderBase extends RecyclerView.ViewHolder {
         private int mViewType;
 
         ViewHolderBase(View itemView, int viewType) {
@@ -2902,7 +2844,7 @@
     /**
      * Add a footer to the list, to support scrolling behavior below the navbar.
      */
-    final class FooterViewHolder extends ViewHolderBase {
+    static final class FooterViewHolder extends ViewHolderBase {
         FooterViewHolder(View itemView, int viewType) {
             super(itemView, viewType);
         }
@@ -3327,7 +3269,8 @@
                 parentGroup.addView(row2);
 
                 mDirectShareViewHolder = new DirectShareViewHolder(parentGroup,
-                        Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType);
+                        Lists.newArrayList(row1, row2), getMaxTargetsPerRow(), viewType,
+                        mChooserMultiProfilePagerAdapter::getActiveListAdapter);
                 loadViewsIntoGroup(mDirectShareViewHolder);
 
                 return mDirectShareViewHolder;
@@ -3489,7 +3432,7 @@
      * {@link ChooserGridAdapter#VIEW_TYPE_DIRECT_SHARE},
      * and {@link ChooserGridAdapter#VIEW_TYPE_CALLER_AND_RANK}.
      */
-    abstract class ItemGroupViewHolder extends ViewHolderBase {
+    abstract static class ItemGroupViewHolder extends ViewHolderBase {
         protected int mMeasuredRowHeight;
         private int[] mItemIndices;
         protected final View[] mCells;
@@ -3539,7 +3482,7 @@
         }
     }
 
-    class SingleRowViewHolder extends ItemGroupViewHolder {
+    static class SingleRowViewHolder extends ItemGroupViewHolder {
         private final ViewGroup mRow;
 
         SingleRowViewHolder(ViewGroup row, int cellCount, int viewType) {
@@ -3573,7 +3516,7 @@
         }
     }
 
-    class DirectShareViewHolder extends ItemGroupViewHolder {
+    static class DirectShareViewHolder extends ItemGroupViewHolder {
         private final ViewGroup mParent;
         private final List<ViewGroup> mRows;
         private int mCellCountPerRow;
@@ -3585,14 +3528,17 @@
 
         private final boolean[] mCellVisibility;
 
+        private final Supplier<ChooserListAdapter> mListAdapterSupplier;
+
         DirectShareViewHolder(ViewGroup parent, List<ViewGroup> rows, int cellCountPerRow,
-                int viewType) {
+                int viewType, Supplier<ChooserListAdapter> listAdapterSupplier) {
             super(rows.size() * cellCountPerRow, parent, viewType);
 
             this.mParent = parent;
             this.mRows = rows;
             this.mCellCountPerRow = cellCountPerRow;
             this.mCellVisibility = new boolean[rows.size() * cellCountPerRow];
+            this.mListAdapterSupplier = listAdapterSupplier;
         }
 
         public ViewGroup addView(int index, View v) {
@@ -3666,8 +3612,7 @@
 
                 // only expand if we have more than maxTargetsPerRow, and delay that decision
                 // until they start to scroll
-                ChooserListAdapter adapter =
-                        mChooserMultiProfilePagerAdapter.getActiveListAdapter();
+                ChooserListAdapter adapter = mListAdapterSupplier.get();
                 int validTargets = adapter.getSelectableServiceTargetCount();
                 if (validTargets <= maxTargetsPerRow) {
                     mHideDirectShareExpansion = true;
diff --git a/core/jni/android_database_SQLiteConnection.cpp b/core/jni/android_database_SQLiteConnection.cpp
index c80f1dc..32697ae 100644
--- a/core/jni/android_database_SQLiteConnection.cpp
+++ b/core/jni/android_database_SQLiteConnection.cpp
@@ -518,23 +518,29 @@
     }
 }
 
-static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
-    int err = sqlite3_step(statement);
-    if (err == SQLITE_ROW) {
+static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement,
+        bool isPragmaStmt) {
+    int rc = sqlite3_step(statement);
+    if (isPragmaStmt) {
+        while (rc == SQLITE_ROW) {
+            rc = sqlite3_step(statement);
+        }
+    }
+    if (rc == SQLITE_ROW) {
         throw_sqlite3_exception(env,
                 "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
-    } else if (err != SQLITE_DONE) {
+    } else if (rc != SQLITE_DONE) {
         throw_sqlite3_exception(env, connection->db);
     }
-    return err;
+    return rc;
 }
 
-static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr,
-        jlong statementPtr) {
+static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr, jlong statementPtr,
+        jboolean isPragmaStmt) {
     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
 
-    executeNonQuery(env, connection, statement);
+    executeNonQuery(env, connection, statement, isPragmaStmt);
 }
 
 static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
@@ -542,7 +548,7 @@
     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
 
-    int err = executeNonQuery(env, connection, statement);
+    int err = executeNonQuery(env, connection, statement, false);
     return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
 }
 
@@ -551,7 +557,7 @@
     SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
     sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
 
-    int err = executeNonQuery(env, connection, statement);
+    int err = executeNonQuery(env, connection, statement, false);
     return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
             ? sqlite3_last_insert_rowid(connection->db) : -1;
 }
@@ -912,7 +918,7 @@
             (void*)nativeBindBlob },
     { "nativeResetStatementAndClearBindings", "(JJ)V",
             (void*)nativeResetStatementAndClearBindings },
-    { "nativeExecute", "(JJ)V",
+    { "nativeExecute", "(JJZ)V",
             (void*)nativeExecute },
     { "nativeExecuteForLong", "(JJ)J",
             (void*)nativeExecuteForLong },
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 3e31b46..d4fb3e3 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -112,11 +112,6 @@
                   transaction);
 }
 
-static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) {
-    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
-    queue->flushShadowQueue();
-}
-
 static void nativeMergeWithNextTransaction(JNIEnv*, jclass clazz, jlong ptr, jlong transactionPtr,
                                            jlong framenumber) {
     sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
@@ -144,6 +139,11 @@
     return queue->getLastAcquiredFrameNum();
 }
 
+static void nativeApplyPendingTransactions(JNIEnv* env, jclass clazz, jlong ptr, jlong frameNum) {
+    sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+    queue->applyPendingTransactions(frameNum);
+}
+
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
         // clang-format off
@@ -152,12 +152,12 @@
         {"nativeDestroy", "(J)V", (void*)nativeDestroy},
         {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction},
         {"nativeUpdate", "(JJJJIJ)V", (void*)nativeUpdate},
-        {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue},
         {"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
         {"nativeSetTransactionCompleteCallback",
                 "(JJLandroid/graphics/BLASTBufferQueue$TransactionCompleteCallback;)V",
                 (void*)nativeSetTransactionCompleteCallback},
         {"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
+        {"nativeApplyPendingTransactions", "(JJ)V", (void*)nativeApplyPendingTransactions},
         // clang-format on
 };
 
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 787d348..aadd320 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -98,6 +98,15 @@
     }
 }
 
+static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr,
+                                            jobject binder)
+{
+    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
+    if (parcel) {
+        parcel->markForBinder(ibinderForJavaObject(env, binder));
+    }
+}
+
 static jint android_os_Parcel_dataSize(jlong nativePtr)
 {
     Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
@@ -749,7 +758,9 @@
 
 static const JNINativeMethod gParcelMethods[] = {
     // @CriticalNative
-    {"nativeMarkSensitive",             "(J)V", (void*)android_os_Parcel_markSensitive},
+    {"nativeMarkSensitive",       "(J)V", (void*)android_os_Parcel_markSensitive},
+    // @FastNative
+    {"nativeMarkForBinder",       "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_markForBinder},
     // @CriticalNative
     {"nativeDataSize",            "(J)I", (void*)android_os_Parcel_dataSize},
     // @CriticalNative
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 953902a..32c57d1 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -63,7 +63,7 @@
     *vector = counter->getCount(state);
 }
 
-static jobject native_toString(JNIEnv *env, jlong nativePtr, jobject self) {
+static jobject native_toString(JNIEnv *env, jobject self, jlong nativePtr) {
     battery::LongArrayMultiStateCounter *counter =
             reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
     return env->NewStringUTF(counter->toString().c_str());
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 313c4da..a4151c7 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -682,9 +682,6 @@
     <dimen name="default_magnifier_horizontal_offset">0dp</dimen>
     <item type="dimen" format="float" name="default_magnifier_zoom">1.25</item>
 
-    <!-- Spacing around the background change frome service to non-service -->
-    <dimen name="chooser_service_spacing">8dp</dimen>
-
     <item type="dimen" name="aerr_padding_list_top">15dp</item>
     <item type="dimen" name="aerr_padding_list_bottom">8dp</item>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7dd94f2..87197ac 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3687,7 +3687,6 @@
   <java-symbol type="string" name="popup_window_default_title" />
   <java-symbol type="bool" name="config_showAreaUpdateInfoSettings" />
   <java-symbol type="layout" name="shutdown_dialog" />
-  <java-symbol type="dimen" name="chooser_service_spacing" />
   <java-symbol type="bool" name="config_showSysuiShutdown" />
   <java-symbol type="drawable" name="chooser_file_generic" />
 
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index 26296f1..2da9f57 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -46,5 +46,10 @@
         counter.getCounts(longArrayContainer, 1);
         longArrayContainer.getValues(result);
         assertThat(result).isEqualTo(new long[]{25, 50, 75, 100});
+
+        assertThat(counter.toString()).isEqualTo(
+                "currentState: 0 lastStateChangeTimestamp: 9000 lastUpdateTimestamp: 9000 states:"
+                        + " [0: time: 0 counter: { 75, 150, 225, 300}"
+                        + ", 1: time: 0 counter: { 25, 50, 75, 100}]");
     }
 }
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 0e1360f..e369acc 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -34,12 +34,12 @@
     private static native void nativeSetNextTransaction(long ptr, long transactionPtr);
     private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
             int format, long transactionPtr);
-    private static native void nativeFlushShadowQueue(long ptr);
     private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
                                                               long frameNumber);
     private static native void nativeSetTransactionCompleteCallback(long ptr, long frameNumber,
             TransactionCompleteCallback callback);
     private static native long nativeGetLastAcquiredFrameNum(long ptr);
+    private static native void nativeApplyPendingTransactions(long ptr, long frameNumber);
 
     /**
      * Callback sent to {@link #setTransactionCompleteCallback(long, TransactionCompleteCallback)}
@@ -125,10 +125,6 @@
         }
     }
 
-    public void flushShadowQueue() {
-        nativeFlushShadowQueue(mNativeObject);
-    }
-
     /**
      * Merge the transaction passed in to the next transaction in BlastBufferQueue. The next
      * transaction will be applied or merged when the next frame with specified frame number
@@ -146,6 +142,17 @@
         nativeMergeWithNextTransaction(mNativeObject, nativeTransaction, frameNumber);
     }
 
+    /**
+     * Apply any transactions that were passed to {@link #mergeWithNextTransaction} with the
+     * specified frameNumber. This is intended to ensure transactions don't get stuck as pending
+     * if the specified frameNumber is never drawn.
+     *
+     * @param frameNumber The frameNumber used to determine which transactions to apply.
+     */
+    public void applyPendingTransactions(long frameNumber) {
+        nativeApplyPendingTransactions(mNativeObject, frameNumber);
+    }
+
     public long getLastAcquiredFrameNum() {
         return nativeGetLastAcquiredFrameNum(mNativeObject);
     }
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 2c299fa..b931d03 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -587,6 +587,7 @@
                 "HardwareBitmapUploader.cpp",
                 "HWUIProperties.sysprop",
                 "JankTracker.cpp",
+                "FrameMetricsReporter.cpp",
                 "Layer.cpp",
                 "LayerUpdateQueue.cpp",
                 "ProfileData.cpp",
@@ -691,6 +692,7 @@
         "tests/unit/FatVectorTests.cpp",
         "tests/unit/GraphicsStatsServiceTests.cpp",
         "tests/unit/JankTrackerTests.cpp",
+        "tests/unit/FrameMetricsReporterTests.cpp",
         "tests/unit/LayerUpdateQueueTests.cpp",
         "tests/unit/LinearAllocatorTests.cpp",
         "tests/unit/MatrixTests.cpp",
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ef1f5aa..498ec57 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -26,6 +26,13 @@
     virtual void notify(const int64_t* buffer) = 0;
     bool waitForPresentTime() const { return mWaitForPresentTime; };
 
+    void reportMetricsFrom(uint64_t frameNumber, int32_t surfaceControlId) {
+        mAttachedFrameNumber = frameNumber;
+        mSurfaceControlId = surfaceControlId;
+    };
+    uint64_t attachedFrameNumber() const { return mAttachedFrameNumber; };
+    int32_t attachedSurfaceControlId() const { return mSurfaceControlId; };
+
     /**
      * Create a new metrics observer. An observer that watches present time gets notified at a
      * different time than the observer that doesn't.
@@ -42,6 +49,22 @@
 
 private:
     const bool mWaitForPresentTime;
+
+    // The id of the surface control (mSurfaceControlGenerationId in CanvasContext)
+    // for which the mAttachedFrameNumber applies to. We rely on this value being
+    // an increasing counter. We will report metrics:
+    // - for all frames if the frame comes from a surface with a surfaceControlId
+    //   that is strictly greater than mSurfaceControlId.
+    // - for all frames with a frame number greater than or equal to mAttachedFrameNumber
+    //   if the frame comes from a surface with a surfaceControlId that is equal to the
+    //   mSurfaceControlId.
+    // We will never report metrics if the frame comes from a surface with a surfaceControlId
+    // that is strictly smaller than mSurfaceControlId.
+    int32_t mSurfaceControlId;
+
+    // The frame number the metrics observer was attached on. Metrics will be sent from this frame
+    // number (inclusive) onwards in the case that the surface id is equal to mSurfaceControlId.
+    uint64_t mAttachedFrameNumber;
 };
 
 }  // namespace uirenderer
diff --git a/libs/hwui/FrameMetricsReporter.cpp b/libs/hwui/FrameMetricsReporter.cpp
new file mode 100644
index 0000000..ee32ea1
--- /dev/null
+++ b/libs/hwui/FrameMetricsReporter.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include "FrameMetricsReporter.h"
+
+namespace android {
+namespace uirenderer {
+
+void FrameMetricsReporter::reportFrameMetrics(const int64_t* stats, bool hasPresentTime,
+                                              uint64_t frameNumber, int32_t surfaceControlId) {
+    FatVector<sp<FrameMetricsObserver>, 10> copy;
+    {
+        std::lock_guard lock(mObserversLock);
+        copy.reserve(mObservers.size());
+        for (size_t i = 0; i < mObservers.size(); i++) {
+            auto observer = mObservers[i];
+
+            if (CC_UNLIKELY(surfaceControlId < observer->attachedSurfaceControlId())) {
+                // Don't notify if the metrics are from a frame that was run on an old
+                // surface (one from before the observer was attached).
+                ALOGV("skipped reporting metrics from old surface %d", surfaceControlId);
+                continue;
+            } else if (CC_UNLIKELY(surfaceControlId == observer->attachedSurfaceControlId() &&
+                                   frameNumber < observer->attachedFrameNumber())) {
+                // Don't notify if the metrics are from a frame that was queued by the
+                // BufferQueueProducer on the render thread before the observer was attached.
+                ALOGV("skipped reporting metrics from old frame %ld", (long)frameNumber);
+                continue;
+            }
+
+            const bool wantsPresentTime = observer->waitForPresentTime();
+            if (hasPresentTime == wantsPresentTime) {
+                copy.push_back(observer);
+            }
+        }
+    }
+    for (size_t i = 0; i < copy.size(); i++) {
+        copy[i]->notify(stats);
+    }
+}
+
+}  // namespace uirenderer
+}  // namespace android
diff --git a/libs/hwui/FrameMetricsReporter.h b/libs/hwui/FrameMetricsReporter.h
index 0ac025f..7e51df7c 100644
--- a/libs/hwui/FrameMetricsReporter.h
+++ b/libs/hwui/FrameMetricsReporter.h
@@ -63,23 +63,14 @@
      * If an observer does not want present time, only notify when 'hasPresentTime' is false.
      * Never notify both types of observers from the same callback, because the callback with
      * 'hasPresentTime' is sent at a different time than the one without.
+     *
+     * The 'frameNumber' and 'surfaceControlId' associated to the frame whose's stats are being
+     * reported are used to determine whether or not the stats should be reported. We won't report
+     * stats of frames that are from "old" surfaces (i.e. with surfaceControlIds older than the one
+     * the observer was attached on) nor those that are from "old" frame numbers.
      */
-    void reportFrameMetrics(const int64_t* stats, bool hasPresentTime) {
-        FatVector<sp<FrameMetricsObserver>, 10> copy;
-        {
-            std::lock_guard lock(mObserversLock);
-            copy.reserve(mObservers.size());
-            for (size_t i = 0; i < mObservers.size(); i++) {
-                const bool wantsPresentTime = mObservers[i]->waitForPresentTime();
-                if (hasPresentTime == wantsPresentTime) {
-                    copy.push_back(mObservers[i]);
-                }
-            }
-        }
-        for (size_t i = 0; i < copy.size(); i++) {
-            copy[i]->notify(stats);
-        }
-    }
+    void reportFrameMetrics(const int64_t* stats, bool hasPresentTime, uint64_t frameNumber,
+                            int32_t surfaceControlId);
 
 private:
     FatVector<sp<FrameMetricsObserver>, 10> mObservers GUARDED_BY(mObserversLock);
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 34e5577..3e5cbb5 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -164,7 +164,8 @@
             - lastFrameOffset + mFrameIntervalLegacy;
 }
 
-void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter) {
+void JankTracker::finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
+                              int64_t frameNumber, int32_t surfaceControlId) {
     std::lock_guard lock(mDataMutex);
 
     calculateLegacyJank(frame);
@@ -253,7 +254,8 @@
     }
 
     if (CC_UNLIKELY(reporter.get() != nullptr)) {
-        reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */);
+        reporter->reportFrameMetrics(frame.data(), false /* hasPresentTime */, frameNumber,
+                                     surfaceControlId);
     }
 }
 
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index bdb784d..bcd031e 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -57,7 +57,8 @@
     }
 
     FrameInfo* startFrame() { return &mFrames.next(); }
-    void finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter);
+    void finishFrame(FrameInfo& frame, std::unique_ptr<FrameMetricsReporter>& reporter,
+                     int64_t frameNumber, int32_t surfaceId);
 
     // Calculates the 'legacy' jank information, i.e. with outdated refresh rate information and
     // without GPU completion or deadlined information.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 0cd6ffb..5fa0922 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -203,9 +203,10 @@
     mSurfaceControl = surfaceControl;
     mSurfaceControlGenerationId++;
     mExpectSurfaceStats = surfaceControl != nullptr;
-    if (mSurfaceControl != nullptr) {
+    if (mExpectSurfaceStats) {
         funcs.acquireFunc(mSurfaceControl);
-        funcs.registerListenerFunc(surfaceControl, this, &onSurfaceStatsAvailable);
+        funcs.registerListenerFunc(surfaceControl, mSurfaceControlGenerationId, this,
+                                   &onSurfaceStatsAvailable);
     }
 }
 
@@ -218,7 +219,7 @@
 
     }
 
-    mFrameNumber = -1;
+    mFrameNumber = 0;
 
     if (mNativeSurface != nullptr && hasSurface) {
         mHaveNewSurface = true;
@@ -512,7 +513,7 @@
                                       mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes,
                                       &(profiler()));
 
-    int64_t frameCompleteNr = getFrameNumber();
+    uint64_t frameCompleteNr = getFrameNumber();
 
     waitOnFences();
 
@@ -580,7 +581,7 @@
         mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = swap.dequeueDuration;
         mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = swap.queueDuration;
         mHaveNewSurface = false;
-        mFrameNumber = -1;
+        mFrameNumber = 0;
     } else {
         mCurrentFrameInfo->set(FrameInfoIndex::DequeueBufferDuration) = 0;
         mCurrentFrameInfo->set(FrameInfoIndex::QueueBufferDuration) = 0;
@@ -613,16 +614,20 @@
     if (requireSwap) {
         if (mExpectSurfaceStats) {
             reportMetricsWithPresentTime();
-            std::lock_guard lock(mLast4FrameInfosMutex);
-            std::pair<FrameInfo*, int64_t>& next = mLast4FrameInfos.next();
-            next.first = mCurrentFrameInfo;
-            next.second = frameCompleteNr;
+            {  // acquire lock
+                std::lock_guard lock(mLast4FrameMetricsInfosMutex);
+                FrameMetricsInfo& next = mLast4FrameMetricsInfos.next();
+                next.frameInfo = mCurrentFrameInfo;
+                next.frameNumber = frameCompleteNr;
+                next.surfaceId = mSurfaceControlGenerationId;
+            }  // release lock
         } else {
             mCurrentFrameInfo->markFrameCompleted();
             mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted)
                     = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted);
             std::scoped_lock lock(mFrameMetricsReporterMutex);
-            mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter);
+            mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr,
+                                     mSurfaceControlGenerationId);
         }
     }
 
@@ -658,14 +663,18 @@
     ATRACE_CALL();
     FrameInfo* forthBehind;
     int64_t frameNumber;
+    int32_t surfaceControlId;
+
     {  // acquire lock
-        std::scoped_lock lock(mLast4FrameInfosMutex);
-        if (mLast4FrameInfos.size() != mLast4FrameInfos.capacity()) {
+        std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
+        if (mLast4FrameMetricsInfos.size() != mLast4FrameMetricsInfos.capacity()) {
             // Not enough frames yet
             return;
         }
-        // Surface object keeps stats for the last 8 frames.
-        std::tie(forthBehind, frameNumber) = mLast4FrameInfos.front();
+        auto frameMetricsInfo = mLast4FrameMetricsInfos.front();
+        forthBehind = frameMetricsInfo.frameInfo;
+        frameNumber = frameMetricsInfo.frameNumber;
+        surfaceControlId = frameMetricsInfo.surfaceId;
     }  // release lock
 
     nsecs_t presentTime = 0;
@@ -680,25 +689,51 @@
     {  // acquire lock
         std::scoped_lock lock(mFrameMetricsReporterMutex);
         if (mFrameMetricsReporter != nullptr) {
-            mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/);
+            mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/,
+                                                      frameNumber, surfaceControlId);
         }
     }  // release lock
 }
 
-FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber) {
-    std::scoped_lock lock(mLast4FrameInfosMutex);
-    for (size_t i = 0; i < mLast4FrameInfos.size(); i++) {
-        if (mLast4FrameInfos[i].second == frameNumber) {
-            return mLast4FrameInfos[i].first;
+void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) {
+    std::scoped_lock lock(mFrameMetricsReporterMutex);
+    if (mFrameMetricsReporter.get() == nullptr) {
+        mFrameMetricsReporter.reset(new FrameMetricsReporter());
+    }
+
+    // We want to make sure we aren't reporting frames that have already been queued by the
+    // BufferQueueProducer on the rendner thread but are still pending the callback to report their
+    // their frame metrics.
+    uint64_t nextFrameNumber = getFrameNumber();
+    observer->reportMetricsFrom(nextFrameNumber, mSurfaceControlGenerationId);
+    mFrameMetricsReporter->addObserver(observer);
+}
+
+void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) {
+    std::scoped_lock lock(mFrameMetricsReporterMutex);
+    if (mFrameMetricsReporter.get() != nullptr) {
+        mFrameMetricsReporter->removeObserver(observer);
+        if (!mFrameMetricsReporter->hasObservers()) {
+            mFrameMetricsReporter.reset(nullptr);
         }
     }
+}
+
+FrameInfo* CanvasContext::getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId) {
+    std::scoped_lock lock(mLast4FrameMetricsInfosMutex);
+    for (size_t i = 0; i < mLast4FrameMetricsInfos.size(); i++) {
+        if (mLast4FrameMetricsInfos[i].frameNumber == frameNumber &&
+            mLast4FrameMetricsInfos[i].surfaceId == surfaceControlId) {
+            return mLast4FrameMetricsInfos[i].frameInfo;
+        }
+    }
+
     return nullptr;
 }
 
-void CanvasContext::onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
-            ASurfaceControlStats* stats) {
-
-    CanvasContext* instance = static_cast<CanvasContext*>(context);
+void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
+                                            ASurfaceControlStats* stats) {
+    auto* instance = static_cast<CanvasContext*>(context);
 
     const ASurfaceControlFunctions& functions =
             instance->mRenderThread.getASurfaceControlFunctions();
@@ -706,14 +741,15 @@
     nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
     uint64_t frameNumber = functions.getFrameNumberFunc(stats);
 
-    FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber);
+    FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId);
 
     if (frameInfo != nullptr) {
         frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime,
                 frameInfo->get(FrameInfoIndex::SwapBuffersCompleted));
         frameInfo->set(FrameInfoIndex::GpuCompleted) = gpuCompleteTime;
         std::scoped_lock lock(instance->mFrameMetricsReporterMutex);
-        instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter);
+        instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber,
+                                           surfaceControlId);
     }
 }
 
@@ -854,9 +890,9 @@
     mFrameFences.push_back(CommonPool::async(std::move(func)));
 }
 
-int64_t CanvasContext::getFrameNumber() {
-    // mFrameNumber is reset to -1 when the surface changes or we swap buffers
-    if (mFrameNumber == -1 && mNativeSurface.get()) {
+uint64_t CanvasContext::getFrameNumber() {
+    // mFrameNumber is reset to 0 when the surface changes or we swap buffers
+    if (mFrameNumber == 0 && mNativeSurface.get()) {
         mFrameNumber = ANativeWindow_getNextFrameId(mNativeSurface->getNativeWindow());
     }
     return mFrameNumber;
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index ed4a620..0caf76a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -167,29 +167,13 @@
 
     void setContentDrawBounds(const Rect& bounds) { mContentDrawBounds = bounds; }
 
-    void addFrameMetricsObserver(FrameMetricsObserver* observer) {
-        std::scoped_lock lock(mFrameMetricsReporterMutex);
-        if (mFrameMetricsReporter.get() == nullptr) {
-            mFrameMetricsReporter.reset(new FrameMetricsReporter());
-        }
-
-        mFrameMetricsReporter->addObserver(observer);
-    }
-
-    void removeFrameMetricsObserver(FrameMetricsObserver* observer) {
-        std::scoped_lock lock(mFrameMetricsReporterMutex);
-        if (mFrameMetricsReporter.get() != nullptr) {
-            mFrameMetricsReporter->removeObserver(observer);
-            if (!mFrameMetricsReporter->hasObservers()) {
-                mFrameMetricsReporter.reset(nullptr);
-            }
-        }
-    }
+    void addFrameMetricsObserver(FrameMetricsObserver* observer);
+    void removeFrameMetricsObserver(FrameMetricsObserver* observer);
 
     // Used to queue up work that needs to be completed before this frame completes
     void enqueueFrameWork(std::function<void()>&& func);
 
-    int64_t getFrameNumber();
+    uint64_t getFrameNumber();
 
     void waitOnFences();
 
@@ -212,8 +196,8 @@
     SkISize getNextFrameSize() const;
 
     // Called when SurfaceStats are available.
-    static void onSurfaceStatsAvailable(void* context, ASurfaceControl* control,
-            ASurfaceControlStats* stats);
+    static void onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
+                                        ASurfaceControlStats* stats);
 
     void setASurfaceTransactionCallback(
             const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
@@ -254,7 +238,13 @@
      */
     void reportMetricsWithPresentTime();
 
-    FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber);
+    struct FrameMetricsInfo {
+        FrameInfo* frameInfo;
+        int64_t frameNumber;
+        int32_t surfaceId;
+    };
+
+    FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
 
     // The same type as Frame.mWidth and Frame.mHeight
     int32_t mLastFrameWidth = 0;
@@ -266,7 +256,10 @@
     // NULL to remove the reference
     ASurfaceControl* mSurfaceControl = nullptr;
     // id to track surface control changes and WebViewFunctor uses it to determine
-    // whether reparenting is needed
+    // whether reparenting is needed also used by FrameMetricsReporter to determine
+    // if a frame is from an "old" surface (i.e. one that existed before the
+    // observer was attched) and therefore shouldn't be reported.
+    // NOTE: It is important that this is an increasing counter.
     int32_t mSurfaceControlGenerationId = 0;
     // stopped indicates the CanvasContext will reject actual redraw operations,
     // and defer repaint until it is un-stopped
@@ -288,7 +281,8 @@
 
     // Need at least 4 because we do quad buffer. Add a 5th for good measure.
     RingBuffer<SwapHistory, 5> mSwapHistory;
-    int64_t mFrameNumber = -1;
+    // Frame numbers start at 1, 0 means uninitialized
+    uint64_t mFrameNumber = 0;
     int64_t mDamageId = 0;
 
     // last vsync for a dropped frame due to stuffed queue
@@ -308,10 +302,11 @@
 
     FrameInfo* mCurrentFrameInfo = nullptr;
 
-    // List of frames that are awaiting GPU completion reporting
-    RingBuffer<std::pair<FrameInfo*, int64_t>, 4> mLast4FrameInfos
-            GUARDED_BY(mLast4FrameInfosMutex);
-    std::mutex mLast4FrameInfosMutex;
+    // List of data of frames that are awaiting GPU completion reporting. Used to compute frame
+    // metrics and determine whether or not to report the metrics.
+    RingBuffer<FrameMetricsInfo, 4> mLast4FrameMetricsInfos
+            GUARDED_BY(mLast4FrameMetricsInfosMutex);
+    std::mutex mLast4FrameMetricsInfosMutex;
 
     std::string mName;
     JankTracker mJankTracker;
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 05d225b..0b81fc04 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -83,8 +83,9 @@
 typedef void (*ASC_acquire)(ASurfaceControl* control);
 typedef void (*ASC_release)(ASurfaceControl* control);
 
-typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, void* context,
-        ASurfaceControl_SurfaceStatsListener func);
+typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, int32_t id,
+                                                 void* context,
+                                                 ASurfaceControl_SurfaceStatsListener func);
 typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
                                                    ASurfaceControl_SurfaceStatsListener func);
 
diff --git a/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
new file mode 100644
index 0000000..fb04700
--- /dev/null
+++ b/libs/hwui/tests/unit/FrameMetricsReporterTests.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <FrameMetricsObserver.h>
+#include <FrameMetricsReporter.h>
+#include <utils/TimeUtils.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+using ::testing::NotNull;
+
+class TestFrameMetricsObserver : public FrameMetricsObserver {
+public:
+    explicit TestFrameMetricsObserver(bool waitForPresentTime)
+            : FrameMetricsObserver(waitForPresentTime){};
+
+    MOCK_METHOD(void, notify, (const int64_t* buffer), (override));
+};
+
+TEST(FrameMetricsReporter, reportsAllFramesIfNoFromFrameIsSpecified) {
+    auto reporter = std::make_shared<FrameMetricsReporter>();
+
+    auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
+    EXPECT_CALL(*observer, notify).Times(4);
+
+    reporter->addObserver(observer.get());
+
+    const int64_t* stats;
+    bool hasPresentTime = false;
+    uint64_t frameNumber = 1;
+    int32_t surfaceControlId = 0;
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    frameNumber = 10;
+    surfaceControlId = 0;
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    frameNumber = 0;
+    surfaceControlId = 2;
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    frameNumber = 10;
+    surfaceControlId = 2;
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, respectsWaitForPresentTimeUnset) {
+    auto reporter = std::make_shared<FrameMetricsReporter>();
+
+    auto observer = sp<TestFrameMetricsObserver>::make(false /*waitForPresentTime*/);
+    reporter->addObserver(observer.get());
+
+    const int64_t* stats;
+    bool hasPresentTime = false;
+    uint64_t frameNumber = 3;
+    int32_t surfaceControlId = 0;
+
+    EXPECT_CALL(*observer, notify).Times(1);
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    EXPECT_CALL(*observer, notify).Times(0);
+    hasPresentTime = true;
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, respectsWaitForPresentTimeSet) {
+    auto reporter = std::make_shared<FrameMetricsReporter>();
+
+    auto observer = sp<TestFrameMetricsObserver>::make(true /*waitForPresentTime*/);
+    reporter->addObserver(observer.get());
+
+    const int64_t* stats;
+    bool hasPresentTime = false;
+    uint64_t frameNumber = 3;
+    int32_t surfaceControlId = 0;
+
+    EXPECT_CALL(*observer, notify).Times(0);
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    EXPECT_CALL(*observer, notify).Times(1);
+    hasPresentTime = true;
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, reportsAllFramesAfterSpecifiedFromFrame) {
+    const int64_t* stats;
+    bool hasPresentTime = false;
+
+    std::vector<uint64_t> frameNumbers{0, 1, 10};
+    std::vector<int32_t> surfaceControlIds{0, 1, 10};
+    for (uint64_t frameNumber : frameNumbers) {
+        for (int32_t surfaceControlId : surfaceControlIds) {
+            auto reporter = std::make_shared<FrameMetricsReporter>();
+
+            auto observer =
+                    sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+            observer->reportMetricsFrom(frameNumber, surfaceControlId);
+            reporter->addObserver(observer.get());
+
+            EXPECT_CALL(*observer, notify).Times(8);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 1, surfaceControlId);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10, surfaceControlId);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId + 1);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1,
+                                         surfaceControlId + 1);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 1,
+                                         surfaceControlId + 1);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10,
+                                         surfaceControlId + 1);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10,
+                                         surfaceControlId + 10);
+        }
+    }
+}
+
+TEST(FrameMetricsReporter, doesNotReportsFramesBeforeSpecifiedFromFrame) {
+    const int64_t* stats;
+    bool hasPresentTime = false;
+
+    std::vector<uint64_t> frameNumbers{1, 10};
+    std::vector<int32_t> surfaceControlIds{0, 1, 10};
+    for (uint64_t frameNumber : frameNumbers) {
+        for (int32_t surfaceControlId : surfaceControlIds) {
+            auto reporter = std::make_shared<FrameMetricsReporter>();
+
+            auto observer =
+                    sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+            observer->reportMetricsFrom(frameNumber, surfaceControlId);
+            reporter->addObserver(observer.get());
+
+            EXPECT_CALL(*observer, notify).Times(0);
+            reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1, surfaceControlId);
+            if (surfaceControlId > 0) {
+                reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber,
+                                             surfaceControlId - 1);
+                reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber - 1,
+                                             surfaceControlId - 1);
+            }
+        }
+    }
+}
+
+TEST(FrameMetricsReporter, canRemoveObservers) {
+    const int64_t* stats;
+    bool hasPresentTime = false;
+    uint64_t frameNumber = 3;
+    int32_t surfaceControlId = 0;
+
+    auto reporter = std::make_shared<FrameMetricsReporter>();
+
+    auto observer = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+
+    observer->reportMetricsFrom(frameNumber, surfaceControlId);
+    reporter->addObserver(observer.get());
+
+    EXPECT_CALL(*observer, notify).Times(1);
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    ASSERT_TRUE(reporter->removeObserver(observer.get()));
+
+    EXPECT_CALL(*observer, notify).Times(0);
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+}
+
+TEST(FrameMetricsReporter, canSupportMultipleObservers) {
+    const int64_t* stats;
+    bool hasPresentTime = false;
+    uint64_t frameNumber = 3;
+    int32_t surfaceControlId = 0;
+
+    auto reporter = std::make_shared<FrameMetricsReporter>();
+
+    auto observer1 = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+    auto observer2 = sp<TestFrameMetricsObserver>::make(hasPresentTime /*waitForPresentTime*/);
+    observer1->reportMetricsFrom(frameNumber, surfaceControlId);
+    observer2->reportMetricsFrom(frameNumber + 10, surfaceControlId + 1);
+    reporter->addObserver(observer1.get());
+    reporter->addObserver(observer2.get());
+
+    EXPECT_CALL(*observer1, notify).Times(1);
+    EXPECT_CALL(*observer2, notify).Times(0);
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber, surfaceControlId);
+
+    EXPECT_CALL(*observer1, notify).Times(1);
+    EXPECT_CALL(*observer2, notify).Times(1);
+    reporter->reportFrameMetrics(stats, hasPresentTime, frameNumber + 10, surfaceControlId + 1);
+}
diff --git a/libs/hwui/tests/unit/JankTrackerTests.cpp b/libs/hwui/tests/unit/JankTrackerTests.cpp
index f467ebf..5b397de 100644
--- a/libs/hwui/tests/unit/JankTrackerTests.cpp
+++ b/libs/hwui/tests/unit/JankTrackerTests.cpp
@@ -34,6 +34,9 @@
     JankTracker jankTracker(&container);
     std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
 
+    uint64_t frameNumber = 0;
+    uint32_t surfaceId = 0;
+
     FrameInfo* info = jankTracker.startFrame();
     info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
     info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -42,7 +45,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 115_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     info = jankTracker.startFrame();
     info->set(FrameInfoIndex::IntendedVsync) = 116_ms;
@@ -52,7 +55,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 131_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(2, container.get()->totalFrameCount());
     ASSERT_EQ(0, container.get()->jankFrameCount());
@@ -65,6 +68,9 @@
     JankTracker jankTracker(&container);
     std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
 
+    uint64_t frameNumber = 0;
+    uint32_t surfaceId = 0;
+
     FrameInfo* info = jankTracker.startFrame();
     info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
     info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -73,7 +79,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->totalFrameCount());
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -85,6 +91,9 @@
     JankTracker jankTracker(&container);
     std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
 
+    uint64_t frameNumber = 0;
+    uint32_t surfaceId = 0;
+
     FrameInfo* info = jankTracker.startFrame();
     info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
     info->set(FrameInfoIndex::Vsync) = 101_ms;
@@ -93,7 +102,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 118_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->totalFrameCount());
     ASSERT_EQ(0, container.get()->jankFrameCount());
@@ -106,6 +115,9 @@
     JankTracker jankTracker(&container);
     std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
 
+    uint64_t frameNumber = 0;
+    uint32_t surfaceId = 0;
+
     // First frame janks
     FrameInfo* info = jankTracker.startFrame();
     info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
@@ -115,7 +127,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
 
@@ -128,7 +140,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(2, container.get()->totalFrameCount());
     ASSERT_EQ(1, container.get()->jankFrameCount());
@@ -140,6 +152,9 @@
     JankTracker jankTracker(&container);
     std::unique_ptr<FrameMetricsReporter> reporter = std::make_unique<FrameMetricsReporter>();
 
+    uint64_t frameNumber = 0;
+    uint32_t surfaceId = 0;
+
     // First frame janks
     FrameInfo* info = jankTracker.startFrame();
     info->set(FrameInfoIndex::IntendedVsync) = 100_ms;
@@ -149,7 +164,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 121_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 120_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
 
@@ -162,7 +177,7 @@
     info->set(FrameInfoIndex::FrameCompleted) = 137_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 136_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(1, container.get()->jankFrameCount());
 
@@ -175,8 +190,8 @@
     info->set(FrameInfoIndex::FrameCompleted) = 169_ms;
     info->set(FrameInfoIndex::FrameInterval) = 16_ms;
     info->set(FrameInfoIndex::FrameDeadline) = 168_ms;
-    jankTracker.finishFrame(*info, reporter);
+    jankTracker.finishFrame(*info, reporter, frameNumber, surfaceId);
 
     ASSERT_EQ(3, container.get()->totalFrameCount());
     ASSERT_EQ(2, container.get()->jankFrameCount());
-}
\ No newline at end of file
+}
diff --git a/media/Android.bp b/media/Android.bp
index ce62b03..7592c15 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -55,12 +55,18 @@
         "aidl/android/media/audio/common/AudioChannelLayout.aidl",
         "aidl/android/media/audio/common/AudioConfig.aidl",
         "aidl/android/media/audio/common/AudioConfigBase.aidl",
+        "aidl/android/media/audio/common/AudioContentType.aidl",
+        "aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl",
         "aidl/android/media/audio/common/AudioEncapsulationMode.aidl",
+        "aidl/android/media/audio/common/AudioEncapsulationType.aidl",
         "aidl/android/media/audio/common/AudioFormatDescription.aidl",
         "aidl/android/media/audio/common/AudioFormatType.aidl",
+        "aidl/android/media/audio/common/AudioMode.aidl",
         "aidl/android/media/audio/common/AudioOffloadInfo.aidl",
+        "aidl/android/media/audio/common/AudioSource.aidl",
         "aidl/android/media/audio/common/AudioStreamType.aidl",
         "aidl/android/media/audio/common/AudioUsage.aidl",
+        "aidl/android/media/audio/common/AudioUuid.aidl",
         "aidl/android/media/audio/common/PcmType.aidl",
     ],
     stability: "vintf",
diff --git a/media/aidl/android/media/audio/common/AudioContentType.aidl b/media/aidl/android/media/audio/common/AudioContentType.aidl
new file mode 100644
index 0000000..50ac181
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioContentType.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Content type specifies "what" is playing. The content type expresses the
+ * general category of the content: speech, music, movie audio, etc.
+ * This enum corresponds to AudioAttributes.CONTENT_TYPE_* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioContentType {
+    /**
+     * Content type value to use when the content type is unknown, or other than
+     * the ones defined.
+     */
+    UNKNOWN = 0,
+    /**
+     * Content type value to use when the content type is speech.
+     */
+    SPEECH = 1,
+    /**
+     * Content type value to use when the content type is music.
+     */
+    MUSIC = 2,
+    /**
+     * Content type value to use when the content type is a soundtrack,
+     * typically accompanying a movie or TV program.
+     */
+    MOVIE = 3,
+    /**
+     * Content type value to use when the content type is a sound used to
+     * accompany a user action, such as a beep or sound effect expressing a key
+     * click, or event, such as the type of a sound for a bonus being received
+     * in a game. These sounds are mostly synthesized or short Foley sounds.
+     */
+    SONIFICATION = 4,
+}
diff --git a/media/aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl b/media/aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl
new file mode 100644
index 0000000..e0b272c
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioEncapsulationMetadataType.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Enumeration of metadata types permitted for use by encapsulation mode audio
+ * streams (see AudioEncapsulationMode). This type corresponds to
+ * AudioTrack.ENCAPSULATION_METADATA_TYPE_* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioEncapsulationMetadataType {
+    /** Default value. */
+    NONE = 0,
+    /**
+     * Encapsulation metadata type for framework tuner information.
+     */
+    FRAMEWORK_TUNER = 1,
+    /**
+     * Encapsulation metadata type for DVB AD descriptor.
+     *
+     * This metadata is formatted per ETSI TS 101 154 Table E.1: AD_descriptor.
+     */
+    DVB_AD_DESCRIPTOR = 2,
+}
diff --git a/media/aidl/android/media/audio/common/AudioEncapsulationType.aidl b/media/aidl/android/media/audio/common/AudioEncapsulationType.aidl
new file mode 100644
index 0000000..9e80bd6
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioEncapsulationType.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 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 android.media.audio.common;
+
+/**
+ * Audio encapsulation type is used to describe if the audio data should be sent
+ * with a particular encapsulation type or not. This enum corresponds to
+ * AudioProfile.AUDIO_ENCAPSULATION_* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioEncapsulationType {
+    /** No encapsulation type is specified. */
+    NONE = 0,
+    /** Encapsulation used the format defined in the standard IEC 61937. */
+    IEC61937 = 1,
+}
diff --git a/media/aidl/android/media/audio/common/AudioMode.aidl b/media/aidl/android/media/audio/common/AudioMode.aidl
new file mode 100644
index 0000000..cc03813
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioMode.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Major modes for a mobile device. The current mode setting affects audio
+ * routing.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioMode {
+    /**
+     * Used as default value in parcelables to indicate that a value was not
+     * set. Should never be considered a valid setting, except for backward
+     * compatibility scenarios.
+     */
+    SYS_RESERVED_INVALID = -2,
+    /**
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
+     */
+    SYS_RESERVED_CURRENT = -1,
+    /** Normal mode (no call in progress). */
+    NORMAL = 0,
+    /** Mobile device is receiving an incoming connection request. */
+    RINGTONE = 1,
+    /** Calls handled by the telephony stack (PSTN). */
+    IN_CALL = 2,
+    /** Calls handled by apps (VoIP). */
+    IN_COMMUNICATION = 3,
+    /** Call screening in progress. */
+    CALL_SCREEN = 4,
+}
diff --git a/media/aidl/android/media/audio/common/AudioSource.aidl b/media/aidl/android/media/audio/common/AudioSource.aidl
new file mode 100644
index 0000000..527ee39
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioSource.aidl
@@ -0,0 +1,90 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Defines the audio source. An audio source defines both a default physical
+ * source of audio signal, and a recording configuration. This enum corresponds
+ * to MediaRecorder.AudioSource.* constants in the SDK.
+ *
+ * {@hide}
+ */
+@Backing(type="int")
+@VintfStability
+enum AudioSource {
+    /**
+     * Used as default value in parcelables to indicate that a value was not
+     * set. Should never be considered a valid setting, except for backward
+     * compatibility scenarios.
+     */
+    SYS_RESERVED_INVALID = -1,
+    /** Default audio source. */
+    DEFAULT = 0,
+    /** Microphone audio source. */
+    MIC = 1,
+    /** Voice call uplink (Tx) audio source. */
+    VOICE_UPLINK = 2,
+    /** Voice call downlink (Rx) audio source. */
+    VOICE_DOWNLINK = 3,
+    /** Voice call uplink + downlink (duplex) audio source. */
+    VOICE_CALL = 4,
+    /**
+     * Microphone audio source tuned for video recording, with the same
+     * orientation as the camera if available.
+     */
+    CAMCORDER = 5,
+    /** Microphone audio source tuned for voice recognition. */
+    VOICE_RECOGNITION = 6,
+    /**
+     * Microphone audio source tuned for voice communications such as VoIP. It
+     * will for instance take advantage of echo cancellation or automatic gain
+     * control if available.
+     */
+    VOICE_COMMUNICATION = 7,
+    /**
+     * Audio source for a submix of audio streams to be presented remotely. An
+     * application can use this audio source to capture a mix of audio streams
+     * that should be transmitted to a remote receiver such as a Wifi display.
+     * While recording is active, these audio streams are redirected to the
+     * remote submix instead of being played on the device speaker or headset.
+     */
+    REMOTE_SUBMIX = 8,
+    /**
+     * Microphone audio source tuned for unprocessed (raw) sound if available,
+     * behaves like DEFAULT otherwise.
+     */
+    UNPROCESSED = 9,
+    /**
+     * Source for capturing audio meant to be processed in real time and played
+     * back for live performance (e.g karaoke). The capture path will minimize
+     * latency and coupling with playback path.
+     */
+    VOICE_PERFORMANCE = 10,
+    /**
+     * Source for an echo canceller to capture the reference signal to be
+     * canceled. The echo reference signal will be captured as close as
+     * possible to the DAC in order to include all post processing applied to
+     * the playback path.
+     */
+    ECHO_REFERENCE = 1997,
+    /** Audio source for capturing broadcast FM tuner output. */
+    FM_TUNER = 1998,
+    /**
+     * A low-priority, preemptible audio source for for background software
+     * hotword detection. Same tuning as VOICE_RECOGNITION.
+     */
+    HOTWORD = 1999,
+}
diff --git a/media/aidl/android/media/audio/common/AudioStreamType.aidl b/media/aidl/android/media/audio/common/AudioStreamType.aidl
index 1fa3a9c..e7f2961 100644
--- a/media/aidl/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl/android/media/audio/common/AudioStreamType.aidl
@@ -37,8 +37,11 @@
      * Indicates that the operation is applied to the "default" stream
      * in this context, e.g. MUSIC in normal device state, or RING if the
      * phone is ringing.
+     *
+     * Value reserved for system use only. HALs must never return this value to
+     * the system or accept it from the system.
      */
-    DEFAULT = -1,
+    SYS_RESERVED_DEFAULT = -1,
     /** Used to identify the volume of audio streams for phone calls. */
     VOICE_CALL = 0,
     /** Used to identify the volume of audio streams for system sounds. */
diff --git a/media/aidl/android/media/audio/common/AudioUuid.aidl b/media/aidl/android/media/audio/common/AudioUuid.aidl
new file mode 100644
index 0000000..f2715ff
--- /dev/null
+++ b/media/aidl/android/media/audio/common/AudioUuid.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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 android.media.audio.common;
+
+/**
+ * Commonly used structure for passing unique identifiers (UUID).
+ * For the definition of UUID, refer to ITU-T X.667 spec.
+ *
+ * {@hide}
+ */
+@JavaDerive(equals=true, toString=true)
+@VintfStability
+parcelable AudioUuid {
+    int timeLow;
+    int timeMid;
+    int timeHiAndVersion;
+    int clockSeq;
+    byte[] node; // Length = 6
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
new file mode 100644
index 0000000..3798b82
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioContentType.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioContentType {
+  UNKNOWN = 0,
+  SPEECH = 1,
+  MUSIC = 2,
+  MOVIE = 3,
+  SONIFICATION = 4,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMetadataType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMetadataType.aidl
new file mode 100644
index 0000000..0ee0dbb
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationMetadataType.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioEncapsulationMetadataType {
+  NONE = 0,
+  FRAMEWORK_TUNER = 1,
+  DVB_AD_DESCRIPTOR = 2,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationType.aidl
new file mode 100644
index 0000000..8a31fc4
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioEncapsulationType.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioEncapsulationType {
+  NONE = 0,
+  IEC61937 = 1,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
new file mode 100644
index 0000000..8ae1c10
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioMode.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioMode {
+  SYS_RESERVED_INVALID = -2,
+  SYS_RESERVED_CURRENT = -1,
+  NORMAL = 0,
+  RINGTONE = 1,
+  IN_CALL = 2,
+  IN_COMMUNICATION = 3,
+  CALL_SCREEN = 4,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
new file mode 100644
index 0000000..d1dfe41
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioSource.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum AudioSource {
+  SYS_RESERVED_INVALID = -1,
+  DEFAULT = 0,
+  MIC = 1,
+  VOICE_UPLINK = 2,
+  VOICE_DOWNLINK = 3,
+  VOICE_CALL = 4,
+  CAMCORDER = 5,
+  VOICE_RECOGNITION = 6,
+  VOICE_COMMUNICATION = 7,
+  REMOTE_SUBMIX = 8,
+  UNPROCESSED = 9,
+  VOICE_PERFORMANCE = 10,
+  ECHO_REFERENCE = 1997,
+  FM_TUNER = 1998,
+  HOTWORD = 1999,
+}
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
index cbca6c5..bcfd374 100644
--- a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioStreamType.aidl
@@ -36,7 +36,7 @@
 @Backing(type="int") @VintfStability
 enum AudioStreamType {
   INVALID = -2,
-  DEFAULT = -1,
+  SYS_RESERVED_DEFAULT = -1,
   VOICE_CALL = 0,
   SYSTEM = 1,
   RING = 2,
diff --git a/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUuid.aidl b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUuid.aidl
new file mode 100644
index 0000000..af307da
--- /dev/null
+++ b/media/aidl_api/android.media.audio.common.types/current/android/media/audio/common/AudioUuid.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.media.audio.common;
+/* @hide */
+@JavaDerive(equals=true, toString=true) @VintfStability
+parcelable AudioUuid {
+  int timeLow;
+  int timeMid;
+  int timeHiAndVersion;
+  int clockSeq;
+  byte[] node;
+}
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 693a027..7f74dd4 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -146,28 +146,24 @@
     uint64_t frameNumber;
 };
 
-void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, void* context,
-        ASurfaceControl_SurfaceStatsListener func) {
-    SurfaceStatsCallback callback = [func](void* callback_context,
-                                                               nsecs_t,
-                                                               const sp<Fence>&,
-                                                               const SurfaceStats& surfaceStats) {
-
+void ASurfaceControl_registerSurfaceStatsListener(ASurfaceControl* control, int32_t id,
+                                                  void* context,
+                                                  ASurfaceControl_SurfaceStatsListener func) {
+    SurfaceStatsCallback callback = [func, id](void* callback_context, nsecs_t, const sp<Fence>&,
+                                               const SurfaceStats& surfaceStats) {
         ASurfaceControlStats aSurfaceControlStats;
 
-        ASurfaceControl* aSurfaceControl =
-                reinterpret_cast<ASurfaceControl*>(surfaceStats.surfaceControl.get());
         aSurfaceControlStats.acquireTime = surfaceStats.acquireTime;
         aSurfaceControlStats.previousReleaseFence = surfaceStats.previousReleaseFence;
         aSurfaceControlStats.frameNumber = surfaceStats.eventStats.frameNumber;
 
-        (*func)(callback_context, aSurfaceControl, &aSurfaceControlStats);
+        (*func)(callback_context, id, &aSurfaceControlStats);
     };
+
     TransactionCompletedListener::getInstance()->addSurfaceStatsListener(context,
             reinterpret_cast<void*>(func), ASurfaceControl_to_SurfaceControl(control), callback);
 }
 
-
 void ASurfaceControl_unregisterSurfaceStatsListener(void* context,
         ASurfaceControl_SurfaceStatsListener func) {
     TransactionCompletedListener::getInstance()->removeSurfaceStatsListener(context,
@@ -662,4 +658,4 @@
     Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
 
     transaction->addTransactionCommittedCallback(callback, context);
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/res/layout/qs_user_dialog_content.xml b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
index 377da45..321fe68 100644
--- a/packages/SystemUI/res/layout/qs_user_dialog_content.xml
+++ b/packages/SystemUI/res/layout/qs_user_dialog_content.xml
@@ -31,7 +31,7 @@
         android:layout_height="wrap_content"
         android:layout_width="0dp"
         android:textAlignment="center"
-        android:text="Select user"
+        android:text="@string/qs_user_switch_dialog_title"
         android:textAppearance="@style/TextAppearance.QSDialog.Title"
         android:layout_marginBottom="32dp"
         sysui:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5545437..8f80421 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3030,4 +3030,7 @@
     <string name="qs_tile_request_dialog_add">Add tile</string>
     <!-- Text for TileService request dialog. Text for button for user to reject adding the tile [CHAR LIMIT=20] -->
     <string name="qs_tile_request_dialog_not_add">Do not add tile</string>
+
+    <!-- Title for User Switch dialog. [CHAR LIMIT=20] -->
+    <string name="qs_user_switch_dialog_title">Select user</string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index bcc0530..0790af9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -42,6 +42,7 @@
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
 import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
 import android.hardware.fingerprint.IUdfpsHbmListener;
 import android.os.Bundle;
@@ -49,6 +50,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.SparseBooleanArray;
 import android.view.MotionEvent;
 import android.view.WindowManager;
 
@@ -76,6 +78,9 @@
 /**
  * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
  * appropriate biometric UI (e.g. BiometricDialogView).
+ *
+ * Also coordinates biometric-related things, such as UDFPS, with
+ * {@link com.android.keyguard.KeyguardUpdateMonitor}
  */
 @SysUISingleton
 public class AuthController extends SystemUI implements CommandQueue.Callbacks,
@@ -115,6 +120,8 @@
     @Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
     @Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
 
+    @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
+
     private class BiometricTaskStackListener extends TaskStackListener {
         @Override
         public void onTaskStackChanged() {
@@ -122,6 +129,21 @@
         }
     }
 
+    private final FingerprintStateListener mFingerprintStateListener =
+            new FingerprintStateListener() {
+        @Override
+        public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
+            Log.d(TAG, "onEnrollmentsChanged, userId: " + userId
+                    + ", sensorId: " + sensorId
+                    + ", hasEnrollments: " + hasEnrollments);
+            for (FingerprintSensorPropertiesInternal prop : mUdfpsProps) {
+                if (prop.sensorId == sensorId) {
+                    mUdfpsEnrolledForUser.put(userId, hasEnrollments);
+                }
+            }
+        }
+    };
+
     @NonNull
     private final IFingerprintAuthenticatorsRegisteredCallback
             mFingerprintAuthenticatorsRegisteredCallback =
@@ -436,6 +458,7 @@
         mUdfpsControllerFactory = udfpsControllerFactory;
         mSidefpsControllerFactory = sidefpsControllerFactory;
         mWindowManager = windowManager;
+        mUdfpsEnrolledForUser = new SparseBooleanArray();
         mOrientationListener = new BiometricOrientationEventListener(context,
                 () -> {
                     onOrientationChanged();
@@ -474,6 +497,7 @@
         if (mFingerprintManager != null) {
             mFingerprintManager.addAuthenticatorsRegisteredCallback(
                     mFingerprintAuthenticatorsRegisteredCallback);
+            mFingerprintManager.registerFingerprintStateListener(mFingerprintStateListener);
         }
 
         mTaskStackListener = new BiometricTaskStackListener();
@@ -673,7 +697,7 @@
             return false;
         }
 
-        return mFingerprintManager.hasEnrolledTemplatesForAnySensor(userId, mUdfpsProps);
+        return mUdfpsEnrolledForUser.get(userId);
     }
 
     private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index c2716b9..a472710 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -75,7 +75,7 @@
     private float mDividerAlpha;
     private int mNotificationHeaderMargin;
 
-    private int mNotificatonTopPadding;
+    private int mNotificationTopPadding;
     private float mCollapsedBottompadding;
     private boolean mChildrenExpanded;
     private ExpandableNotificationRow mContainingNotification;
@@ -140,9 +140,9 @@
         mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
         mNotificationHeaderMargin = res.getDimensionPixelSize(
                 R.dimen.notification_children_container_margin_top);
-        mNotificatonTopPadding = res.getDimensionPixelSize(
+        mNotificationTopPadding = res.getDimensionPixelSize(
                 R.dimen.notification_children_container_top_padding);
-        mHeaderHeight = mNotificationHeaderMargin + mNotificatonTopPadding;
+        mHeaderHeight = mNotificationHeaderMargin + mNotificationTopPadding;
         mCollapsedBottompadding = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin);
         mEnableShadowOnChildNotifications =
@@ -203,7 +203,7 @@
                     newHeightSpec);
         }
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
-        int height = mNotificationHeaderMargin + mNotificatonTopPadding;
+        int height = mNotificationHeaderMargin + mNotificationTopPadding;
         int childCount =
                 Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
         int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
@@ -526,11 +526,11 @@
                 if (mUserLocked) {
                     intrinsicHeight += NotificationUtils.interpolate(
                             0,
-                            mNotificatonTopPadding + mDividerHeight,
+                            mNotificationTopPadding + mDividerHeight,
                             expandFactor);
                 } else {
                     intrinsicHeight += childrenExpanded
-                            ? mNotificatonTopPadding + mDividerHeight
+                            ? mNotificationTopPadding + mDividerHeight
                             : 0;
                 }
                 firstChild = false;
@@ -583,10 +583,10 @@
                 if (expandingToExpandedGroup) {
                     yPosition += NotificationUtils.interpolate(
                             0,
-                            mNotificatonTopPadding + mDividerHeight,
+                            mNotificationTopPadding + mDividerHeight,
                             expandFactor);
                 } else {
-                    yPosition += mChildrenExpanded ? mNotificatonTopPadding + mDividerHeight : 0;
+                    yPosition += mChildrenExpanded ? mNotificationTopPadding + mDividerHeight : 0;
                 }
                 firstChild = false;
             }
@@ -1009,7 +1009,7 @@
                     /* likeHighPriority */);
         }
         int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificatonTopPadding;
+                + mNotificationTopPadding;
         int visibleChildren = 0;
         int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -1072,7 +1072,7 @@
 
     private int getVisibleChildrenExpandHeight() {
         int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificatonTopPadding + mDividerHeight;
+                + mNotificationTopPadding + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mAttachedChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -1212,7 +1212,7 @@
 
     public int getPositionInLinearLayout(View childInGroup) {
         int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificatonTopPadding;
+                + mNotificationTopPadding;
 
         for (int i = 0; i < mAttachedChildren.size(); i++) {
             ExpandableNotificationRow child = mAttachedChildren.get(i);
diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
index 0a1c77b..9764a16 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -26,6 +26,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.NoSuchElementException;
 
 /**
@@ -70,26 +72,32 @@
     }
 
     /** Holder for wrapping multiple handlers into a single Callback. */
-    protected static class CompositeCallback implements Callback {
+    public static class CompositeCallback implements Callback {
         @NonNull
-        private final Callback[] mCallbacks;
+        private final List<Callback> mCallbacks;
 
         public CompositeCallback(@NonNull Callback... callbacks) {
-            mCallbacks = callbacks;
+            mCallbacks = new ArrayList<>();
+
+            for (Callback callback : callbacks) {
+                if (callback != null) {
+                    mCallbacks.add(callback);
+                }
+            }
         }
 
         @Override
         public final void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-            for (int i = 0; i < mCallbacks.length; i++) {
-                mCallbacks[i].onClientStarted(clientMonitor);
+            for (int i = 0; i < mCallbacks.size(); i++) {
+                mCallbacks.get(i).onClientStarted(clientMonitor);
             }
         }
 
         @Override
         public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                 boolean success) {
-            for (int i = mCallbacks.length - 1; i >= 0; i--) {
-                mCallbacks[i].onClientFinished(clientMonitor, success);
+            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
+                mCallbacks.get(i).onClientFinished(clientMonitor, success);
             }
         }
     }
@@ -256,7 +264,7 @@
         return mToken;
     }
 
-    public final int getSensorId() {
+    public int getSensorId() {
         return mSensorId;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index a15e14b..9191b8b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -31,7 +31,7 @@
 /**
  * A class to keep track of the enrollment state for a given client.
  */
-public abstract class EnrollClient<T> extends AcquisitionClient<T> {
+public abstract class EnrollClient<T> extends AcquisitionClient<T> implements EnrollmentModifier {
 
     private static final String TAG = "Biometrics/EnrollClient";
 
@@ -40,6 +40,7 @@
     protected final BiometricUtils mBiometricUtils;
 
     private long mEnrollmentStartTimeMs;
+    private final boolean mHasEnrollmentsBeforeStarting;
 
     /**
      * @return true if the user has already enrolled the maximum number of templates.
@@ -56,6 +57,18 @@
         mBiometricUtils = utils;
         mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
         mTimeoutSec = timeoutSec;
+        mHasEnrollmentsBeforeStarting = hasEnrollments();
+    }
+
+    @Override
+    public boolean hasEnrollmentStateChanged() {
+        final boolean hasEnrollmentsNow = hasEnrollments();
+        return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting;
+    }
+
+    @Override
+    public boolean hasEnrollments() {
+        return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
     }
 
     public void onEnrollResult(BiometricAuthenticator.Identifier identifier, int remaining) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
new file mode 100644
index 0000000..c2f909b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors;
+
+/**
+ * Interface for {@link BaseClientMonitor} subclasses that affect the state of enrollment.
+ */
+public interface EnrollmentModifier {
+
+    /**
+     * Callers should typically check this after
+     * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)}
+     *
+     * @return true if the user has gone from:
+     *      1) none-enrolled --> enrolled
+     *      2) enrolled --> none-enrolled
+     *      but NOT any-enrolled --> more-enrolled
+     */
+    boolean hasEnrollmentStateChanged();
+
+    /**
+     * @return true if the user has any enrollments
+     */
+    boolean hasEnrollments();
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index 282261e..579dfd6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -40,7 +40,8 @@
  * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
  */
 public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Identifier, T>
-        extends HalClientMonitor<T> implements EnumerateConsumer, RemovalConsumer {
+        extends HalClientMonitor<T> implements EnumerateConsumer, RemovalConsumer,
+        EnrollmentModifier {
 
     private static final String TAG = "Biometrics/InternalCleanupClient";
 
@@ -61,6 +62,7 @@
     private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
     private final List<S> mEnrolledList;
+    private final boolean mHasEnrollmentsBeforeStarting;
     private BaseClientMonitor mCurrentTask;
 
     private final Callback mEnumerateCallback = new Callback() {
@@ -115,6 +117,7 @@
         mBiometricUtils = utils;
         mAuthenticatorIds = authenticatorIds;
         mEnrolledList = enrolledList;
+        mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
     }
 
     private void startCleanupUnknownHalTemplates() {
@@ -166,6 +169,18 @@
     }
 
     @Override
+    public boolean hasEnrollmentStateChanged() {
+        final boolean hasEnrollmentsNow = !mBiometricUtils
+                .getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+        return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting;
+    }
+
+    @Override
+    public boolean hasEnrollments() {
+        return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+    }
+
+    @Override
     public void onEnumerationResult(BiometricAuthenticator.Identifier identifier,
             int remaining) {
         if (!(mCurrentTask instanceof InternalEnumerateClient)) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 383efce..2a6677e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -33,12 +33,13 @@
  * A class to keep track of the remove state for a given client.
  */
 public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T>
-        extends HalClientMonitor<T> implements RemovalConsumer {
+        extends HalClientMonitor<T> implements RemovalConsumer, EnrollmentModifier {
 
     private static final String TAG = "Biometrics/RemovalClient";
 
     private final BiometricUtils<S> mBiometricUtils;
     private final Map<Integer, Long> mAuthenticatorIds;
+    private final boolean mHasEnrollmentsBeforeStarting;
 
     public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
             @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
@@ -49,6 +50,7 @@
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricUtils = utils;
         mAuthenticatorIds = authenticatorIds;
+        mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty();
     }
 
     @Override
@@ -91,6 +93,18 @@
     }
 
     @Override
+    public boolean hasEnrollmentStateChanged() {
+        final boolean hasEnrollmentsNow = !mBiometricUtils
+                .getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+        return hasEnrollmentsNow != mHasEnrollmentsBeforeStarting;
+    }
+
+    @Override
+    public boolean hasEnrollments() {
+        return !mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()).isEmpty();
+    }
+
+    @Override
     public int getProtoEnum() {
         return BiometricsProto.CM_REMOVE;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index fa02c51..af8e8c2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -35,6 +35,7 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
 import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.BiometricsProtoEnums;
@@ -62,11 +63,13 @@
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Pair;
@@ -111,6 +114,7 @@
     private final FingerprintServiceWrapper mServiceWrapper;
     @NonNull private final List<ServiceProvider> mServiceProviders;
     @NonNull private final FingerprintStateCallback mFingerprintStateCallback;
+    @NonNull private final Handler mHandler;
 
     @GuardedBy("mLock")
     @NonNull private final RemoteCallbackList<IFingerprintAuthenticatorsRegisteredCallback>
@@ -125,6 +129,37 @@
      */
     public void registerFingerprintStateListener(@NonNull IFingerprintStateListener listener) {
         mFingerprintStateCallback.registerFingerprintStateListener(listener);
+        broadcastCurrentEnrollmentState(listener);
+    }
+
+    /**
+     * @param listener if non-null, notifies only this listener. if null, notifies all listeners
+     *                 in {@link FingerprintStateCallback}. This is slightly ugly, but reduces
+     *                 redundant code.
+     */
+    private void broadcastCurrentEnrollmentState(@Nullable IFingerprintStateListener listener) {
+        final UserManager um = UserManager.get(getContext());
+        synchronized (mLock) {
+            // Update the new listener with current state of all sensors
+            for (FingerprintSensorPropertiesInternal prop : mSensorProps) {
+                final ServiceProvider provider = getProviderForSensor(prop.sensorId);
+                for (UserInfo userInfo : um.getAliveUsers()) {
+                    final boolean enrolled = !provider
+                            .getEnrolledFingerprints(prop.sensorId, userInfo.id).isEmpty();
+
+                    // Defer this work and allow the loop to release the lock sooner
+                    mHandler.post(() -> {
+                        if (listener != null) {
+                            mFingerprintStateCallback.notifyFingerprintEnrollmentStateChanged(
+                                    listener, userInfo.id, prop.sensorId, enrolled);
+                        } else {
+                            mFingerprintStateCallback.notifyAllFingerprintEnrollmentStateChanged(
+                                    userInfo.id, prop.sensorId, enrolled);
+                        }
+                    });
+                }
+            }
+        }
     }
 
     /**
@@ -143,8 +178,7 @@
                 return null;
             }
 
-            return provider.createTestSession(sensorId, callback, mFingerprintStateCallback,
-                    opPackageName);
+            return provider.createTestSession(sensorId, callback, opPackageName);
         }
 
         @Override
@@ -227,7 +261,7 @@
             }
 
             provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
-                    receiver, opPackageName, enrollReason, mFingerprintStateCallback);
+                    receiver, opPackageName, enrollReason);
         }
 
         @Override // Binder call
@@ -306,7 +340,7 @@
             }
             return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
                     0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName,
-                    restricted, statsClient, isKeyguard, mFingerprintStateCallback);
+                    restricted, statsClient, isKeyguard);
         }
 
         private long authenticateWithPrompt(
@@ -414,7 +448,7 @@
 
             return provider.second.scheduleFingerDetect(provider.first, token, userId,
                     new ClientMonitorCallbackConverter(receiver), opPackageName,
-                    BiometricsProtoEnums.CLIENT_KEYGUARD, mFingerprintStateCallback);
+                    BiometricsProtoEnums.CLIENT_KEYGUARD);
         }
 
         @Override // Binder call
@@ -433,7 +467,7 @@
             provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
                     new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, requestId,
                     restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
-                    allowBackgroundAuthentication, mFingerprintStateCallback);
+                    allowBackgroundAuthentication);
         }
 
         @Override // Binder call
@@ -687,27 +721,6 @@
                     .isEmpty();
         }
 
-        @Override // Binder call
-        public boolean hasEnrolledTemplatesForAnySensor(int userId,
-                @NonNull List<FingerprintSensorPropertiesInternal> sensors,
-                @NonNull String opPackageName) {
-            Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
-
-            for (FingerprintSensorPropertiesInternal prop : sensors) {
-                final ServiceProvider provider = getProviderForSensor(prop.sensorId);
-                if (provider == null) {
-                    Slog.w(TAG, "Null provider for sensorId: " + prop.sensorId
-                            + ", caller: " + opPackageName);
-                    continue;
-                }
-
-                if (!provider.getEnrolledFingerprints(prop.sensorId, userId).isEmpty()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
         public boolean hasEnrolledFingerprints(int sensorId, int userId, String opPackageName) {
             Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
 
@@ -797,10 +810,12 @@
                         && Settings.Secure.getIntForUser(getContext().getContentResolver(),
                         Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
                         UserHandle.USER_CURRENT) != 0) {
-                    fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(), hidlSensor,
+                    fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
+                            mFingerprintStateCallback, hidlSensor,
                             mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                 } else {
-                    fingerprint21 = Fingerprint21.newInstance(getContext(), hidlSensor,
+                    fingerprint21 = Fingerprint21.newInstance(getContext(),
+                            mFingerprintStateCallback, hidlSensor,
                             mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
                 }
                 mServiceProviders.add(fingerprint21);
@@ -823,8 +838,9 @@
                 try {
                     final SensorProps[] props = fp.getSensorProps();
                     final FingerprintProvider provider =
-                            new FingerprintProvider(getContext(), props, instance,
-                                    mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+                            new FingerprintProvider(getContext(), mFingerprintStateCallback, props,
+                                    instance, mLockoutResetDispatcher,
+                                    mGestureAvailabilityDispatcher);
                     mServiceProviders.add(provider);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Remote exception in getSensorProps: " + fqName);
@@ -878,6 +894,7 @@
                     }
                 }
 
+                broadcastCurrentEnrollmentState(null); // broadcasts to all listeners
                 broadcastAllAuthenticatorsRegistered();
             });
         }
@@ -975,6 +992,7 @@
         mFingerprintStateCallback = new FingerprintStateCallback();
         mAuthenticatorsRegisteredCallbacks = new RemoteCallbackList<>();
         mSensorProps = new ArrayList<>();
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     // Notifies the callbacks that all of the authenticators have been registered and removes the
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
index 5f998d8..0050a89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallback.java
@@ -23,6 +23,7 @@
 import static android.hardware.fingerprint.FingerprintStateListener.STATE_KEYGUARD_AUTH;
 
 import android.annotation.NonNull;
+import android.content.Context;
 import android.hardware.fingerprint.FingerprintStateListener;
 import android.hardware.fingerprint.IFingerprintStateListener;
 import android.os.RemoteException;
@@ -31,6 +32,9 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.EnrollClient;
+import com.android.server.biometrics.sensors.EnrollmentModifier;
+import com.android.server.biometrics.sensors.RemovalConsumer;
 import com.android.server.biometrics.sensors.fingerprint.hidl.FingerprintEnrollClient;
 
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -39,9 +43,11 @@
  * A callback for receiving notifications about changes in fingerprint state.
  */
 public class FingerprintStateCallback implements BaseClientMonitor.Callback {
-    private @FingerprintStateListener.State int mFingerprintState;
+
     @NonNull private final CopyOnWriteArrayList<IFingerprintStateListener>
-        mFingerprintStateListeners = new CopyOnWriteArrayList<>();
+            mFingerprintStateListeners = new CopyOnWriteArrayList<>();
+
+    private @FingerprintStateListener.State int mFingerprintState;
 
     public FingerprintStateCallback() {
         mFingerprintState = STATE_IDLE;
@@ -54,8 +60,9 @@
     @Override
     public void onClientStarted(@NonNull BaseClientMonitor client) {
         final int previousFingerprintState = mFingerprintState;
+
         if (client instanceof AuthenticationClient) {
-            AuthenticationClient authClient = (AuthenticationClient) client;
+            final AuthenticationClient<?> authClient = (AuthenticationClient<?>) client;
             if (authClient.isKeyguard()) {
                 mFingerprintState = STATE_KEYGUARD_AUTH;
             } else if (authClient.isBiometricPrompt()) {
@@ -70,6 +77,7 @@
                     "Other authentication client: " + Utils.getClientName(client));
             mFingerprintState = STATE_IDLE;
         }
+
         Slog.d(FingerprintService.TAG, "Fps state updated from " + previousFingerprintState
                 + " to " + mFingerprintState + ", client " + client);
         notifyFingerprintStateListeners(mFingerprintState);
@@ -81,6 +89,18 @@
         Slog.d(FingerprintService.TAG,
                 "Client finished, fps state updated to " + mFingerprintState + ", client "
                         + client);
+
+        if (client instanceof EnrollmentModifier) {
+            EnrollmentModifier enrollmentModifier = (EnrollmentModifier) client;
+            final boolean enrollmentStateChanged = enrollmentModifier.hasEnrollmentStateChanged();
+            Slog.d(FingerprintService.TAG, "Enrollment state changed: " + enrollmentStateChanged);
+            if (enrollmentStateChanged) {
+                notifyAllFingerprintEnrollmentStateChanged(client.getTargetUserId(),
+                        client.getSensorId(),
+                        enrollmentModifier.hasEnrollments());
+            }
+        }
+
         notifyFingerprintStateListeners(mFingerprintState);
     }
 
@@ -95,6 +115,32 @@
     }
 
     /**
+     * This should be invoked when:
+     *  1) Enrolled --> None-enrolled
+     *  2) None-enrolled --> enrolled
+     *  3) HAL becomes ready
+     *  4) Listener is registered
+     */
+    void notifyAllFingerprintEnrollmentStateChanged(int userId, int sensorId,
+            boolean hasEnrollments) {
+        for (IFingerprintStateListener listener : mFingerprintStateListeners) {
+            notifyFingerprintEnrollmentStateChanged(listener, userId, sensorId, hasEnrollments);
+        }
+    }
+
+    /**
+     * Notifies the listener of enrollment state changes.
+     */
+    void notifyFingerprintEnrollmentStateChanged(@NonNull IFingerprintStateListener listener,
+            int userId, int sensorId, boolean hasEnrollments) {
+        try {
+            listener.onEnrollmentsChanged(userId, sensorId, hasEnrollments);
+        } catch (RemoteException e) {
+            Slog.e(FingerprintService.TAG, "Remote exception", e);
+        }
+    }
+
+    /**
      * Enables clients to register a FingerprintStateListener. Used by FingerprintService to forward
      * updates in fingerprint sensor state to the SideFpNsEventHandler
      * @param listener
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index b9fcd8e..1772f81 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
@@ -90,27 +90,23 @@
      */
     void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
             int userId, @NonNull IFingerprintServiceReceiver receiver,
-            @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason,
-            @NonNull FingerprintStateCallback fingerprintStateCallback);
+            @NonNull String opPackageName, @FingerprintManager.EnrollReason int enrollReason);
 
     void cancelEnrollment(int sensorId, @NonNull IBinder token);
 
     long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
-            int statsClient,
-            @NonNull FingerprintStateCallback fingerprintStateCallback);
+            int statsClient);
 
     void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication,
-            @NonNull FingerprintStateCallback fingerprintStateCallback);
+            boolean allowBackgroundAuthentication);
 
     long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
             int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication,
-            @NonNull FingerprintStateCallback fingerprintStateCallback);
+            boolean allowBackgroundAuthentication);
 
     void startPreparedClient(int sensorId, int cookie);
 
@@ -169,6 +165,5 @@
 
     @NonNull
     ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
-            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull String opPackageName);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
index 29f2f20..2b50b96 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java
@@ -143,8 +143,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
-                mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
-                mFingerprintStateCallback);
+                mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index 377feca..ca83dda 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -82,6 +82,7 @@
     private boolean mTestHalEnabled;
 
     @NonNull private final Context mContext;
+    @NonNull private final FingerprintStateCallback mFingerprintStateCallback;
     @NonNull private final String mHalInstanceName;
     @NonNull @VisibleForTesting
     final SparseArray<Sensor> mSensors; // Map of sensors that this HAL supports
@@ -130,10 +131,13 @@
         }
     }
 
-    public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props,
-            @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+    public FingerprintProvider(@NonNull Context context,
+            @NonNull FingerprintStateCallback fingerprintStateCallback,
+            @NonNull SensorProps[] props, @NonNull String halInstanceName,
+            @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
         mContext = context;
+        mFingerprintStateCallback = fingerprintStateCallback;
         mHalInstanceName = halInstanceName;
         mSensors = new SparseArray<>();
         mHandler = new Handler(Looper.getMainLooper());
@@ -335,8 +339,7 @@
     public void scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
-            @FingerprintManager.EnrollReason int enrollReason,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            @FingerprintManager.EnrollReason int enrollReason) {
         mHandler.post(() -> {
             final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties()
                     .maxEnrollmentsPerUser;
@@ -350,13 +353,13 @@
 
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-                    fingerprintStateCallback.onClientStarted(clientMonitor);
+                    mFingerprintStateCallback.onClientStarted(clientMonitor);
                 }
 
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
-                    fingerprintStateCallback.onClientFinished(clientMonitor, success);
+                    mFingerprintStateCallback.onClientFinished(clientMonitor, success);
                     if (success) {
                         scheduleLoadAuthenticatorIdsForUser(sensorId, userId);
                         scheduleInvalidationRequest(sensorId, userId);
@@ -374,17 +377,15 @@
     @Override
     public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName,
-            int statsClient,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            int statsClient) {
         final long id = mRequestCounter.incrementAndGet();
-
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FingerprintDetectClient client = new FingerprintDetectClient(mContext,
                     mSensors.get(sensorId).getLazySession(), token, id, callback, userId,
                     opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric,
                     statsClient);
-            scheduleForSensor(sensorId, client, fingerprintStateCallback);
+            scheduleForSensor(sensorId, client, mFingerprintStateCallback);
         });
 
         return id;
@@ -394,8 +395,7 @@
     public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            boolean allowBackgroundAuthentication) {
         mHandler.post(() -> {
             final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
             final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient(
@@ -405,7 +405,7 @@
                     mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
                     mUdfpsOverlayController, allowBackgroundAuthentication,
                     mSensors.get(sensorId).getSensorProperties());
-            scheduleForSensor(sensorId, client, fingerprintStateCallback);
+            scheduleForSensor(sensorId, client, mFingerprintStateCallback);
         });
     }
 
@@ -413,13 +413,11 @@
     public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
             @NonNull String opPackageName, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            boolean allowBackgroundAuthentication) {
         final long id = mRequestCounter.incrementAndGet();
 
         scheduleAuthenticate(sensorId, token, operationId, userId, cookie, callback,
-                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
-                fingerprintStateCallback);
+                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
 
         return id;
     }
@@ -466,7 +464,7 @@
                     new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId,
                     opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
                     mSensors.get(sensorId).getAuthenticatorIds());
-            scheduleForSensor(sensorId, client);
+            scheduleForSensor(sensorId, client, mFingerprintStateCallback);
         });
     }
 
@@ -481,7 +479,8 @@
                             mContext.getOpPackageName(), sensorId, enrolledList,
                             FingerprintUtils.getInstance(sensorId),
                             mSensors.get(sensorId).getAuthenticatorIds());
-            scheduleForSensor(sensorId, client, callback);
+            scheduleForSensor(sensorId, client, new BaseClientMonitor.CompositeCallback(callback,
+                    mFingerprintStateCallback));
         });
     }
 
@@ -626,9 +625,8 @@
     @NonNull
     @Override
     public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
-            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull String opPackageName) {
-        return mSensors.get(sensorId).createTestSession(callback, fingerprintStateCallback);
+        return mSensors.get(sensorId).createTestSession(callback, mFingerprintStateCallback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index c00daff..79c6b1b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
@@ -143,8 +143,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         mFingerprint21.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
-                mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL,
-                mFingerprintStateCallback);
+                mContext.getOpPackageName(), FingerprintManager.ENROLL_ENROLL);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index f17bcc8..d2882aa4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -102,6 +102,7 @@
     private boolean mTestHalEnabled;
 
     final Context mContext;
+    @NonNull private final FingerprintStateCallback mFingerprintStateCallback;
     private final ActivityTaskManager mActivityTaskManager;
     @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties;
     private final BiometricScheduler mScheduler;
@@ -317,11 +318,13 @@
     }
 
     Fingerprint21(@NonNull Context context,
+            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull HalResultController controller) {
         mContext = context;
+        mFingerprintStateCallback = fingerprintStateCallback;
 
         mSensorProperties = sensorProps;
         mSensorId = sensorProps.sensorId;
@@ -351,6 +354,7 @@
     }
 
     public static Fingerprint21 newInstance(@NonNull Context context,
+            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
@@ -362,8 +366,8 @@
         final HalResultController controller = new HalResultController(sensorProps.sensorId,
                 context, handler,
                 scheduler);
-        return new Fingerprint21(context, sensorProps, scheduler, handler, lockoutResetDispatcher,
-                controller);
+        return new Fingerprint21(context, fingerprintStateCallback, sensorProps, scheduler, handler,
+                lockoutResetDispatcher, controller);
     }
 
     @Override
@@ -557,8 +561,7 @@
     public void scheduleEnroll(int sensorId, @NonNull IBinder token,
             @NonNull byte[] hardwareAuthToken, int userId,
             @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName,
-            @FingerprintManager.EnrollReason int enrollReason,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            @FingerprintManager.EnrollReason int enrollReason) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -570,13 +573,13 @@
             mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-                    fingerprintStateCallback.onClientStarted(clientMonitor);
+                    mFingerprintStateCallback.onClientStarted(clientMonitor);
                 }
 
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
-                    fingerprintStateCallback.onClientFinished(clientMonitor, success);
+                    mFingerprintStateCallback.onClientFinished(clientMonitor, success);
                     if (success) {
                         // Update authenticatorIds
                         scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId(),
@@ -597,10 +600,8 @@
     @Override
     public long scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId,
             @NonNull ClientMonitorCallbackConverter listener, @NonNull String opPackageName,
-            int statsClient,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            int statsClient) {
         final long id = mRequestCounter.incrementAndGet();
-
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -609,7 +610,7 @@
                     mLazyDaemon, token, id, listener, userId, opPackageName,
                     mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric,
                     statsClient);
-            mScheduler.scheduleClientMonitor(client, fingerprintStateCallback);
+            mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
         });
 
         return id;
@@ -619,8 +620,7 @@
     public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
             @NonNull String opPackageName, long requestId, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            boolean allowBackgroundAuthentication) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -631,7 +631,7 @@
                     mSensorProperties.sensorId, isStrongBiometric, statsClient,
                     mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
                     allowBackgroundAuthentication, mSensorProperties);
-            mScheduler.scheduleClientMonitor(client, fingerprintStateCallback);
+            mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
         });
     }
 
@@ -639,13 +639,11 @@
     public long scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
             int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
             @NonNull String opPackageName, boolean restricted, int statsClient,
-            boolean allowBackgroundAuthentication,
-            @NonNull FingerprintStateCallback fingerprintStateCallback) {
+            boolean allowBackgroundAuthentication) {
         final long id = mRequestCounter.incrementAndGet();
 
         scheduleAuthenticate(sensorId, token, operationId, userId, cookie, listener,
-                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication,
-                fingerprintStateCallback);
+                opPackageName, id, restricted, statsClient, allowBackgroundAuthentication);
 
         return id;
     }
@@ -672,7 +670,7 @@
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId,
                     userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId),
                     mSensorProperties.sensorId, mAuthenticatorIds);
-            mScheduler.scheduleClientMonitor(client);
+            mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
         });
     }
 
@@ -689,7 +687,7 @@
                     0 /* fingerprintId */, userId, opPackageName,
                     FingerprintUtils.getLegacyInstance(mSensorId),
                     mSensorProperties.sensorId, mAuthenticatorIds);
-            mScheduler.scheduleClientMonitor(client);
+            mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback);
         });
     }
 
@@ -711,7 +709,8 @@
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
             @Nullable BaseClientMonitor.Callback callback) {
-        scheduleInternalCleanup(userId, callback);
+        scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback,
+                mFingerprintStateCallback));
     }
 
     @Override
@@ -919,9 +918,8 @@
     @NonNull
     @Override
     public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
-            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull String opPackageName) {
         return new BiometricTestSessionImpl(mContext, mSensorProperties.sensorId, callback,
-                fingerprintStateCallback, this, mHalResultController);
+                mFingerprintStateCallback, this, mHalResultController);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
index 24ce867..79ad8e1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java
@@ -26,6 +26,7 @@
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
 import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Handler;
 import android.os.IBinder;
@@ -42,6 +43,7 @@
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
 import java.util.ArrayList;
@@ -270,6 +272,7 @@
     }
 
     public static Fingerprint21UdfpsMock newInstance(@NonNull Context context,
+            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
@@ -280,8 +283,8 @@
                 new TestableBiometricScheduler(TAG, gestureAvailabilityDispatcher);
         final MockHalResultController controller =
                 new MockHalResultController(sensorProps.sensorId, context, handler, scheduler);
-        return new Fingerprint21UdfpsMock(context, sensorProps, scheduler, handler,
-                lockoutResetDispatcher, controller);
+        return new Fingerprint21UdfpsMock(context, fingerprintStateCallback, sensorProps, scheduler,
+                handler, lockoutResetDispatcher, controller);
     }
 
     private static abstract class FakeFingerRunnable implements Runnable {
@@ -400,17 +403,19 @@
             // internal preemption logic is not run.
             mFingerprint21.scheduleAuthenticate(mFingerprint21.mSensorProperties.sensorId, token,
                     operationId, user, cookie, listener, opPackageName, restricted, statsClient,
-                    isKeyguard, null /* fingerprintStateCallback */);
+                    isKeyguard);
         }
     }
 
     private Fingerprint21UdfpsMock(@NonNull Context context,
+            @NonNull FingerprintStateCallback fingerprintStateCallback,
             @NonNull FingerprintSensorPropertiesInternal sensorProps,
             @NonNull TestableBiometricScheduler scheduler,
             @NonNull Handler handler,
             @NonNull LockoutResetDispatcher lockoutResetDispatcher,
             @NonNull MockHalResultController controller) {
-        super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller);
+        super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
+                lockoutResetDispatcher, controller);
         mScheduler = scheduler;
         mScheduler.init(this);
         mHandler = handler;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 383b392..806a5dd 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -19,7 +19,6 @@
 import static android.Manifest.permission.CONTROL_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
 import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
-import static android.os.Process.THREAD_PRIORITY_DISPLAY;
 
 import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
 import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -36,7 +35,6 @@
 import android.hardware.devicestate.IDeviceStateManagerCallback;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -48,8 +46,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
-import com.android.server.ServiceThread;
 import com.android.server.SystemService;
 import com.android.server.policy.DeviceStatePolicyImpl;
 import com.android.server.wm.ActivityTaskManagerInternal;
@@ -93,10 +91,9 @@
     private static final boolean DEBUG = false;
 
     private final Object mLock = new Object();
-    // Internal system service thread used to dispatch calls to the policy and to registered
+    // Handler on the {@link DisplayThread} used to dispatch calls to the policy and to registered
     // callbacks though its handler (mHandler). Provides a guarantee of callback order when
     // leveraging mHandler and also enables posting messages with the service lock held.
-    private final HandlerThread mHandlerThread;
     private final Handler mHandler;
     @NonNull
     private final DeviceStatePolicy mDeviceStatePolicy;
@@ -149,11 +146,10 @@
     @VisibleForTesting
     DeviceStateManagerService(@NonNull Context context, @NonNull DeviceStatePolicy policy) {
         super(context);
-        // Service thread assigned THREAD_PRIORITY_DISPLAY because this service indirectly drives
+        // We use the DisplayThread because this service indirectly drives
         // display (on/off) and window (position) events through its callbacks.
-        mHandlerThread = new ServiceThread(TAG, THREAD_PRIORITY_DISPLAY, false /* allowIo */);
-        mHandlerThread.start();
-        mHandler = mHandlerThread.getThreadHandler();
+        DisplayThread displayThread = DisplayThread.get();
+        mHandler = new Handler(displayThread.getLooper());
         mOverrideRequestController = new OverrideRequestController(
                 this::onOverrideRequestStatusChangedLocked);
         mDeviceStatePolicy = policy;
@@ -552,7 +548,7 @@
             }
 
             ProcessRecord record = new ProcessRecord(callback, pid, this::handleProcessDied,
-                    mHandlerThread.getThreadHandler());
+                    mHandler);
             try {
                 callback.asBinder().linkToDeath(record, 0);
             } catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
index a3134a0..a3f1435 100644
--- a/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
+++ b/services/core/java/com/android/server/pm/DefaultCrossProfileIntentFiltersUtils.java
@@ -199,6 +199,30 @@
                     .addDataType("video/*")
                     .build();
 
+    // TODO(b/199068419): Remove once GEM enables the intent for Googlers
+    /** Pick images can be forwarded to work profile. */
+    private static final DefaultCrossProfileIntentFilter ACTION_PICK_IMAGES_TO_PROFILE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */ 0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(MediaStore.ACTION_PICK_IMAGES)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .build();
+    // TODO(b/199068419): Remove once GEM enables the intent for Googlers
+    /** Pick images can be forwarded to work profile. */
+    private static final DefaultCrossProfileIntentFilter
+            ACTION_PICK_IMAGES_WITH_DATA_TYPES_TO_PROFILE =
+            new DefaultCrossProfileIntentFilter.Builder(
+                    DefaultCrossProfileIntentFilter.Direction.TO_PROFILE,
+                    /* flags= */ 0,
+                    /* letsPersonalDataIntoProfile= */ true)
+                    .addAction(MediaStore.ACTION_PICK_IMAGES)
+                    .addCategory(Intent.CATEGORY_DEFAULT)
+                    .addDataType("image/*")
+                    .addDataType("video/*")
+                    .build();
+
     /** Open document intent can be forwarded to parent user. */
     private static final DefaultCrossProfileIntentFilter OPEN_DOCUMENT =
             new DefaultCrossProfileIntentFilter.Builder(
@@ -312,6 +336,8 @@
                 ACTION_PICK_DATA,
                 ACTION_PICK_IMAGES,
                 ACTION_PICK_IMAGES_WITH_DATA_TYPES,
+                ACTION_PICK_IMAGES_TO_PROFILE,
+                ACTION_PICK_IMAGES_WITH_DATA_TYPES_TO_PROFILE,
                 OPEN_DOCUMENT,
                 GET_CONTENT,
                 USB_DEVICE_ATTACHED,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index a9ddd35..5b205cd 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16966,6 +16966,19 @@
     }
 
     @Override
+    public void clearOrganizationIdForUser(int userHandle) {
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+        synchronized (getLockObject()) {
+            final ActiveAdmin owner = getDeviceOrProfileOwnerAdminLocked(userHandle);
+            owner.mOrganizationId = null;
+            owner.mEnrollmentSpecificId = null;
+            saveSettingsLocked(userHandle);
+        }
+    }
+
+    @Override
     public UserHandle createAndProvisionManagedProfile(
             @NonNull ManagedProfileProvisioningParams provisioningParams,
             @NonNull String callerPackage) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
new file mode 100644
index 0000000..09b5c5c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+
+@Presubmit
+@SmallTest
+public class CompositeCallbackTest {
+
+    @Test
+    public void testNullCallback() {
+        BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+        BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+        BaseClientMonitor.Callback callback3 = null;
+
+        BaseClientMonitor.CompositeCallback callback = new BaseClientMonitor.CompositeCallback(
+                callback1, callback2, callback3);
+
+        BaseClientMonitor clientMonitor = mock(BaseClientMonitor.class);
+
+        callback.onClientStarted(clientMonitor);
+        verify(callback1).onClientStarted(eq(clientMonitor));
+        verify(callback2).onClientStarted(eq(clientMonitor));
+
+        callback.onClientFinished(clientMonitor, true /* success */);
+        verify(callback1).onClientFinished(eq(clientMonitor), eq(true));
+        verify(callback2).onClientFinished(eq(clientMonitor), eq(true));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java
new file mode 100644
index 0000000..38e8dfa
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintStateCallbackTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 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.biometrics.sensors.fingerprint;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.fingerprint.FingerprintStateListener;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.sensors.AuthenticationClient;
+import com.android.server.biometrics.sensors.EnrollClient;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class FingerprintStateCallbackTest {
+
+    private FingerprintStateCallback mCallback;
+
+    @Mock
+    FingerprintStateListener mFingerprintStateListener;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        mCallback = new FingerprintStateCallback();
+        mCallback.registerFingerprintStateListener(mFingerprintStateListener);
+    }
+
+    @Test
+    public void testNoEnrollmentsToEnrollments_callbackNotified() {
+        testEnrollmentCallback(true /* changed */, true /* isNowEnrolled */,
+                true /* expectCallback */, true /* expectedCallbackValue */);
+    }
+
+    @Test
+    public void testEnrollmentsToNoEnrollments_callbackNotified() {
+        testEnrollmentCallback(true /* changed */, false /* isNowEnrolled */,
+                true /* expectCallback */, false /* expectedCallbackValue */);
+    }
+
+    @Test
+    public void testEnrollmentsToEnrollments_callbackNotNotified() {
+        testEnrollmentCallback(false /* changed */, true /* isNowEnrolled */,
+                false /* expectCallback */, false /* expectedCallbackValue */);
+    }
+
+    private void testEnrollmentCallback(boolean changed, boolean isNowEnrolled,
+            boolean expectCallback, boolean expectedCallbackValue) {
+        EnrollClient<?> client = mock(EnrollClient.class);
+
+        final int userId = 10;
+        final int sensorId = 100;
+
+        when(client.hasEnrollmentStateChanged()).thenReturn(changed);
+        when(client.hasEnrollments()).thenReturn(isNowEnrolled);
+        when(client.getTargetUserId()).thenReturn(userId);
+        when(client.getSensorId()).thenReturn(sensorId);
+
+        mCallback.onClientFinished(client, true /* success */);
+        if (expectCallback) {
+            verify(mFingerprintStateListener).onEnrollmentsChanged(eq(userId), eq(sensorId),
+                    eq(expectedCallbackValue));
+        } else {
+            verify(mFingerprintStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
+                    anyBoolean());
+        }
+    }
+
+    @Test
+    public void testAuthentication_enrollmentCallbackNeverNotified() {
+        AuthenticationClient<?> client = mock(AuthenticationClient.class);
+        mCallback.onClientFinished(client, true /* success */);
+        verify(mFingerprintStateListener, never()).onEnrollmentsChanged(anyInt(), anyInt(),
+                anyBoolean());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
index 35c37ef..b51918e 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java
@@ -37,6 +37,7 @@
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
 
 import org.junit.Before;
@@ -58,6 +59,8 @@
     private UserManager mUserManager;
     @Mock
     private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
+    @Mock
+    private FingerprintStateCallback mFingerprintStateCallback;
 
     private SensorProps[] mSensorProps;
     private LockoutResetDispatcher mLockoutResetDispatcher;
@@ -87,8 +90,8 @@
 
         mLockoutResetDispatcher = new LockoutResetDispatcher(mContext);
 
-        mFingerprintProvider = new TestableFingerprintProvider(mContext, mSensorProps, TAG,
-                mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
+        mFingerprintProvider = new TestableFingerprintProvider(mContext, mFingerprintStateCallback,
+                mSensorProps, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
     }
 
     @SuppressWarnings("rawtypes")
@@ -133,11 +136,12 @@
 
     private static class TestableFingerprintProvider extends FingerprintProvider {
         public TestableFingerprintProvider(@NonNull Context context,
+                @NonNull FingerprintStateCallback fingerprintStateCallback,
                 @NonNull SensorProps[] props,
                 @NonNull String halInstanceName,
                 @NonNull LockoutResetDispatcher lockoutResetDispatcher,
                 @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
-            super(context, props, halInstanceName, lockoutResetDispatcher,
+            super(context, fingerprintStateCallback, props, halInstanceName, lockoutResetDispatcher,
                     gestureAvailabilityDispatcher);
         }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
index 0a0dcc9..f6b9209 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21Test.java
@@ -41,6 +41,7 @@
 import com.android.internal.R;
 import com.android.server.biometrics.sensors.BiometricScheduler;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -67,6 +68,8 @@
     Fingerprint21.HalResultController mHalResultController;
     @Mock
     private BiometricScheduler mScheduler;
+    @Mock
+    private FingerprintStateCallback mFingerprintStateCallback;
 
     private LockoutResetDispatcher mLockoutResetDispatcher;
     private Fingerprint21 mFingerprint21;
@@ -96,8 +99,9 @@
                         componentInfo, FingerprintSensorProperties.TYPE_UNKNOWN,
                         resetLockoutRequiresHardwareAuthToken);
 
-        mFingerprint21 = new TestableFingerprint21(mContext, sensorProps, mScheduler,
-                new Handler(Looper.getMainLooper()), mLockoutResetDispatcher, mHalResultController);
+        mFingerprint21 = new TestableFingerprint21(mContext, mFingerprintStateCallback, sensorProps,
+                mScheduler, new Handler(Looper.getMainLooper()), mLockoutResetDispatcher,
+                mHalResultController);
     }
 
     @Test
@@ -118,11 +122,13 @@
     private static class TestableFingerprint21 extends Fingerprint21 {
 
         TestableFingerprint21(@NonNull Context context,
+                @NonNull FingerprintStateCallback fingerprintStateCallback,
                 @NonNull FingerprintSensorPropertiesInternal sensorProps,
                 @NonNull BiometricScheduler scheduler, @NonNull Handler handler,
                 @NonNull LockoutResetDispatcher lockoutResetDispatcher,
                 @NonNull HalResultController controller) {
-            super(context, sensorProps, scheduler, handler, lockoutResetDispatcher, controller);
+            super(context, fingerprintStateCallback, sensorProps, scheduler, handler,
+                    lockoutResetDispatcher, controller);
         }
 
         @Override