Merge "Call TrustManager#reportUserRequestedUnlock"
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 4de8ec8..dd102bd 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -892,7 +892,8 @@
                 }
                 if (history.bucketExpiryTimesMs != null) {
                     xml.startTag(null, TAG_BUCKET_EXPIRY_TIMES);
-                    for (int j = 0; j < history.bucketExpiryTimesMs.size(); ++j) {
+                    final int size = history.bucketExpiryTimesMs.size();
+                    for (int j = 0; j < size; ++j) {
                         final long expiryTimeMs = history.bucketExpiryTimesMs.valueAt(j);
                         // Skip writing to disk if the expiry time already elapsed.
                         if (expiryTimeMs < elapsedTimeMs) {
@@ -994,7 +995,8 @@
             return;
         }
         idpw.print("(");
-        for (int i = 0; i < appUsageHistory.bucketExpiryTimesMs.size(); ++i) {
+        final int size = appUsageHistory.bucketExpiryTimesMs.size();
+        for (int i = 0; i < size; ++i) {
             final int bucket = appUsageHistory.bucketExpiryTimesMs.keyAt(i);
             final long expiryTimeMs = appUsageHistory.bucketExpiryTimesMs.valueAt(i);
             if (i != 0) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 776f374..4aee690 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -125,11 +125,13 @@
     field public static final String POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS";
     field @Deprecated public static final String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
     field public static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
+    field public static final String READ_ASSISTANT_APP_SEARCH_DATA = "android.permission.READ_ASSISTANT_APP_SEARCH_DATA";
     field public static final String READ_BASIC_PHONE_STATE = "android.permission.READ_BASIC_PHONE_STATE";
     field public static final String READ_CALENDAR = "android.permission.READ_CALENDAR";
     field public static final String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
     field public static final String READ_CONTACTS = "android.permission.READ_CONTACTS";
     field public static final String READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE";
+    field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
     field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
     field public static final String READ_LOGS = "android.permission.READ_LOGS";
     field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
@@ -8852,11 +8854,12 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean isDiscovering();
     method public boolean isEnabled();
     method public boolean isLe2MPhySupported();
+    method public int isLeAudioBroadcastAssistantSupported();
+    method public int isLeAudioBroadcastSourceSupported();
     method public int isLeAudioSupported();
     method public boolean isLeCodedPhySupported();
     method public boolean isLeExtendedAdvertisingSupported();
     method public boolean isLePeriodicAdvertisingSupported();
-    method public int isLePeriodicAdvertisingSyncTransferSenderSupported();
     method public boolean isMultipleAdvertisementSupported();
     method public boolean isOffloadedFilteringSupported();
     method public boolean isOffloadedScanBatchingSupported();
@@ -9758,6 +9761,7 @@
     field public static final String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
     field public static final int GATT = 7; // 0x7
     field public static final int GATT_SERVER = 8; // 0x8
+    field public static final int HAP_CLIENT = 28; // 0x1c
     field public static final int HEADSET = 1; // 0x1
     field @Deprecated public static final int HEALTH = 3; // 0x3
     field public static final int HEARING_AID = 21; // 0x15
@@ -10933,10 +10937,10 @@
     method @Nullable public abstract android.content.Intent registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter, @Nullable String, @Nullable android.os.Handler, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void removeStickyBroadcast(@RequiresPermission android.content.Intent);
     method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void removeStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
+    method public void revokeOwnPermissionOnKill(@NonNull String);
+    method public void revokeOwnPermissionsOnKill(@NonNull java.util.Collection<java.lang.String>);
     method public abstract void revokeUriPermission(android.net.Uri, int);
     method public abstract void revokeUriPermission(String, android.net.Uri, int);
-    method public void selfRevokePermission(@NonNull String);
-    method public void selfRevokePermissions(@NonNull java.util.Collection<java.lang.String>);
     method public abstract void sendBroadcast(@RequiresPermission android.content.Intent);
     method public abstract void sendBroadcast(@RequiresPermission android.content.Intent, @Nullable String);
     method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
@@ -11064,8 +11068,8 @@
     field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service";
     field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification";
     field public static final String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
-    field public static final String TV_IAPP_SERVICE = "tv_iapp";
     field public static final String TV_INPUT_SERVICE = "tv_input";
+    field public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
     field public static final String UI_MODE_SERVICE = "uimode";
     field public static final String USAGE_STATS_SERVICE = "usagestats";
     field public static final String USB_SERVICE = "usb";
@@ -18051,6 +18055,7 @@
     field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
     field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
     field public static final long USAGE_CPU_WRITE_RARELY = 32L; // 0x20L
+    field public static final long USAGE_FRONT_BUFFER = 1L; // 0x1L
     field public static final long USAGE_GPU_COLOR_OUTPUT = 512L; // 0x200L
     field public static final long USAGE_GPU_CUBE_MAP = 33554432L; // 0x2000000L
     field public static final long USAGE_GPU_DATA_BUFFER = 16777216L; // 0x1000000L
@@ -22077,14 +22082,30 @@
   public class ImageWriter implements java.lang.AutoCloseable {
     method public void close();
     method public android.media.Image dequeueInputImage();
+    method public long getDataSpace();
     method public int getFormat();
+    method public int getHardwareBufferFormat();
+    method public int getHeight();
     method public int getMaxImages();
+    method public long getUsage();
+    method public int getWidth();
     method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int);
     method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int);
     method public void queueInputImage(android.media.Image);
     method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler);
   }
 
+  public static final class ImageWriter.Builder {
+    ctor public ImageWriter.Builder(@NonNull android.view.Surface);
+    method @NonNull public android.media.ImageWriter build();
+    method @NonNull public android.media.ImageWriter.Builder setDataSpace(long);
+    method @NonNull public android.media.ImageWriter.Builder setHardwareBufferFormat(int);
+    method @NonNull public android.media.ImageWriter.Builder setImageFormat(int);
+    method @NonNull public android.media.ImageWriter.Builder setMaxImages(@IntRange(from=1) int);
+    method @NonNull public android.media.ImageWriter.Builder setUsage(long);
+    method @NonNull public android.media.ImageWriter.Builder setWidthAndHeight(@IntRange(from=1) int, @IntRange(from=1) int);
+  }
+
   public static interface ImageWriter.OnImageReleasedListener {
     method public void onImageReleased(android.media.ImageWriter);
   }
@@ -22496,6 +22517,7 @@
     field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
     field public static final String FEATURE_AdaptivePlayback = "adaptive-playback";
     field public static final String FEATURE_DynamicTimestamp = "dynamic-timestamp";
+    field public static final String FEATURE_EncodingStatistics = "encoding-statistics";
     field public static final String FEATURE_FrameParsing = "frame-parsing";
     field public static final String FEATURE_IntraRefresh = "intra-refresh";
     field public static final String FEATURE_LowLatency = "low-latency";
@@ -23270,6 +23292,7 @@
     field public static final String KEY_OPERATING_RATE = "operating-rate";
     field public static final String KEY_OUTPUT_REORDER_DEPTH = "output-reorder-depth";
     field public static final String KEY_PCM_ENCODING = "pcm-encoding";
+    field public static final String KEY_PICTURE_TYPE = "picture-type";
     field public static final String KEY_PIXEL_ASPECT_RATIO_HEIGHT = "sar-height";
     field public static final String KEY_PIXEL_ASPECT_RATIO_WIDTH = "sar-width";
     field public static final String KEY_PREPEND_HEADER_TO_SYNC_FRAMES = "prepend-sps-pps-to-idr-frames";
@@ -23287,6 +23310,8 @@
     field public static final String KEY_TILE_HEIGHT = "tile-height";
     field public static final String KEY_TILE_WIDTH = "tile-width";
     field public static final String KEY_TRACK_ID = "track-id";
+    field public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL = "video-encoding-statistics-level";
+    field public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average";
     field public static final String KEY_VIDEO_QP_B_MAX = "video-qp-b-max";
     field public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
     field public static final String KEY_VIDEO_QP_I_MAX = "video-qp-i-max";
@@ -23347,12 +23372,18 @@
     field public static final String MIMETYPE_VIDEO_SCRAMBLED = "video/scrambled";
     field public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
     field public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+    field public static final int PICTURE_TYPE_B = 3; // 0x3
+    field public static final int PICTURE_TYPE_I = 1; // 0x1
+    field public static final int PICTURE_TYPE_P = 2; // 0x2
+    field public static final int PICTURE_TYPE_UNKNOWN = 0; // 0x0
     field public static final int TYPE_BYTE_BUFFER = 5; // 0x5
     field public static final int TYPE_FLOAT = 3; // 0x3
     field public static final int TYPE_INTEGER = 1; // 0x1
     field public static final int TYPE_LONG = 2; // 0x2
     field public static final int TYPE_NULL = 0; // 0x0
     field public static final int TYPE_STRING = 4; // 0x4
+    field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1; // 0x1
+    field public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0; // 0x0
   }
 
   public final class MediaMetadata implements android.os.Parcelable {
@@ -26849,16 +26880,43 @@
 
 package android.media.tv.interactive {
 
-  public final class TvIAppManager {
+  public final class TvInteractiveAppInfo implements android.os.Parcelable {
+    ctor public TvInteractiveAppInfo(@NonNull android.content.Context, @NonNull android.content.ComponentName);
+    method public int describeContents();
+    method @NonNull public String getId();
+    method @Nullable public android.content.pm.ServiceInfo getServiceInfo();
+    method @NonNull public int getSupportedTypes();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.interactive.TvInteractiveAppInfo> CREATOR;
+    field public static final int INTERACTIVE_APP_TYPE_ATSC = 2; // 0x2
+    field public static final int INTERACTIVE_APP_TYPE_GINGA = 4; // 0x4
+    field public static final int INTERACTIVE_APP_TYPE_HBBTV = 1; // 0x1
   }
 
-  public abstract class TvIAppService extends android.app.Service {
-    ctor public TvIAppService();
+  public final class TvInteractiveAppManager {
+    method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppInfo> getTvInteractiveAppServiceList();
+  }
+
+  public abstract class TvInteractiveAppService extends android.app.Service {
+    ctor public TvInteractiveAppService();
     method public final android.os.IBinder onBind(android.content.Intent);
-    field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvIAppService";
+    field public static final String SERVICE_INTERFACE = "android.media.tv.interactive.TvInteractiveAppService";
     field public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
   }
 
+  public class TvInteractiveAppView extends android.view.ViewGroup {
+    ctor public TvInteractiveAppView(@NonNull android.content.Context);
+    ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet);
+    ctor public TvInteractiveAppView(@NonNull android.content.Context, @Nullable android.util.AttributeSet, int);
+    method public void clearCallback();
+    method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppView.TvInteractiveAppCallback);
+    method public void startInteractiveApp();
+  }
+
+  public abstract static class TvInteractiveAppView.TvInteractiveAppCallback {
+    ctor public TvInteractiveAppView.TvInteractiveAppCallback();
+  }
+
 }
 
 package android.mtp {
@@ -32236,7 +32294,7 @@
     method @Deprecated public void readMap(@NonNull java.util.Map, @Nullable ClassLoader);
     method public <K, V> void readMap(@NonNull java.util.Map<? super K,? super V>, @Nullable ClassLoader, @NonNull Class<K>, @NonNull Class<V>);
     method @Deprecated @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader);
-    method @Nullable public <T> T readParcelable(@Nullable ClassLoader, @NonNull Class<T>);
+    method @Nullable public <T extends android.os.Parcelable> T readParcelable(@Nullable ClassLoader, @NonNull Class<? super T>);
     method @Deprecated @Nullable public android.os.Parcelable[] readParcelableArray(@Nullable ClassLoader);
     method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
     method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
@@ -32246,7 +32304,7 @@
     method @Nullable public android.os.PersistableBundle readPersistableBundle();
     method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
     method @Deprecated @Nullable public java.io.Serializable readSerializable();
-    method @Nullable public <T> T readSerializable(@Nullable ClassLoader, @NonNull Class<T>);
+    method @Nullable public <T extends java.io.Serializable> T readSerializable(@Nullable ClassLoader, @NonNull Class<? super T>);
     method @NonNull public android.util.Size readSize();
     method @NonNull public android.util.SizeF readSizeF();
     method @Deprecated @Nullable public <T> android.util.SparseArray<T> readSparseArray(@Nullable ClassLoader);
@@ -32516,6 +32574,7 @@
     method public static final boolean is64Bit();
     method public static boolean isApplicationUid(int);
     method public static final boolean isIsolated();
+    method public static final boolean isSupplemental();
     method public static final void killProcess(int);
     method public static final int myPid();
     method @NonNull public static String myProcessName();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index fe53e63..b082629 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -389,6 +389,9 @@
   }
 
   public class Process {
+    method public static final boolean isSupplemental(int);
+    method public static final int toAppUid(int);
+    method public static final int toSupplementalUid(int);
     field public static final int NFC_UID = 1027; // 0x403
     field public static final int VPN_UID = 1016; // 0x3f8
   }
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 465c1f8..cf20b4c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1064,6 +1064,7 @@
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
     field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
     field public static final String ACTION_BIND_SECONDARY_LOCKSCREEN_SERVICE = "android.app.action.BIND_SECONDARY_LOCKSCREEN_SERVICE";
+    field @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE) public static final String ACTION_ESTABLISH_NETWORK_CONNECTION = "android.app.action.ESTABLISH_NETWORK_CONNECTION";
     field public static final String ACTION_PROVISION_FINALIZATION = "android.app.action.PROVISION_FINALIZATION";
     field public static final String ACTION_PROVISION_FINANCED_DEVICE = "android.app.action.PROVISION_FINANCED_DEVICE";
     field public static final String ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE = "android.app.action.PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE";
@@ -2238,6 +2239,7 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean isEncrypted();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean removeBond();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setLowLatencyAudioAllowed(boolean);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMessageAccessPermission(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setPhonebookAccessPermission(int);
@@ -2387,6 +2389,7 @@
     field @NonNull public static final android.os.ParcelUuid COORDINATED_SET;
     field @NonNull public static final android.os.ParcelUuid DIP;
     field @NonNull public static final android.os.ParcelUuid GENERIC_MEDIA_CONTROL;
+    field @NonNull public static final android.os.ParcelUuid HAS;
     field @NonNull public static final android.os.ParcelUuid HEARING_AID;
     field @NonNull public static final android.os.ParcelUuid HFP;
     field @NonNull public static final android.os.ParcelUuid HFP_AG;
@@ -2603,10 +2606,32 @@
 package android.companion.virtual {
 
   public final class VirtualDeviceManager {
+    method @Nullable @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.VirtualDeviceManager.VirtualDevice createVirtualDevice(int, @NonNull android.companion.virtual.VirtualDeviceParams);
   }
 
   public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
     method public void close();
+    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(int, int, int, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
+  }
+
+  public final class VirtualDeviceParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getLockState();
+    method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
+    field public static final int LOCK_STATE_ALWAYS_LOCKED = 0; // 0x0
+    field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
+  }
+
+  public static final class VirtualDeviceParams.Builder {
+    ctor public VirtualDeviceParams.Builder();
+    method @NonNull public android.companion.virtual.VirtualDeviceParams build();
+    method @NonNull @RequiresPermission(value="android.permission.ADD_ALWAYS_UNLOCKED_DISPLAY", conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
+    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
   }
 
 }
@@ -3660,6 +3685,7 @@
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
     method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfigurationForDisplay(@NonNull android.hardware.display.BrightnessConfiguration, @NonNull String);
     method @Deprecated @RequiresPermission(android.Manifest.permission.CONTROL_DISPLAY_SATURATION) public void setSaturationLevel(float);
+    field public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1024; // 0x400
   }
 
 }
@@ -6594,6 +6620,7 @@
     method @Nullable public android.media.tv.tuner.Lnb openLnbByName(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.LnbCallback);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_SHARED_FILTER) public static android.media.tv.tuner.filter.SharedFilter openSharedFilter(@NonNull android.content.Context, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.filter.SharedFilterCallback);
     method @Nullable public android.media.tv.tuner.filter.TimeFilter openTimeFilter();
+    method public int removeOutputPid(@IntRange(from=0) int);
     method public int scan(@NonNull android.media.tv.tuner.frontend.FrontendSettings, int, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.ScanCallback);
     method public int setLnaEnabled(boolean);
     method public int setMaxNumberOfFrontends(int, @IntRange(from=0) int);
@@ -9697,9 +9724,9 @@
     method @BinderThread public void onOneTimePermissionSessionTimeout(@NonNull String);
     method @Deprecated @BinderThread public void onRestoreDelayedRuntimePermissionsBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @Deprecated @BinderThread public void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
+    method @BinderThread public void onRevokeOwnPermissionsOnKill(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String, @NonNull Runnable);
     method @BinderThread public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.util.List<java.lang.String>>>);
-    method @BinderThread public void onSelfRevokePermissions(@NonNull String, @NonNull java.util.List<java.lang.String>, @NonNull Runnable);
     method @Deprecated @BinderThread public abstract void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull android.permission.AdminPermissionControlParams, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @BinderThread public void onStageAndApplyRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream, @NonNull Runnable);
@@ -11069,6 +11096,7 @@
     method @android.service.persistentdata.PersistentDataBlockManager.FlashLockState @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public int getFlashLockState();
     method public long getMaximumDataBlockSize();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_OEM_UNLOCK_STATE, "android.permission.OEM_UNLOCK_STATE"}) public boolean getOemUnlockEnabled();
+    method @NonNull public String getPersistentDataPackageName();
     method public byte[] read();
     method @Deprecated @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void setOemUnlockEnabled(boolean);
     method @RequiresPermission("android.permission.OEM_UNLOCK_STATE") public void wipe();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0e52c5d..e97ef6c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1928,6 +1928,9 @@
     method @NonNull public static String convert(@NonNull java.util.UUID);
     method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
     method public static boolean isUserKeyUnlocked(int);
+    field public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
+    field public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
+    field public static final String STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
   }
 
   public final class StorageVolume implements android.os.Parcelable {
diff --git a/core/java/android/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index a8ba1d3..bb2b8d4 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -24,6 +24,8 @@
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 
+import java.util.LinkedList;
+import java.util.Queue;
 import java.util.concurrent.Executor;
 
 /**
@@ -102,6 +104,11 @@
     private boolean mServiceDetectsGestures;
     /** Map of callbacks to executors. Lazily created when adding the first callback. */
     private ArrayMap<Callback, Executor> mCallbacks;
+    // A list of motion events that should be queued until a pending transition has taken place.
+    private Queue<MotionEvent> mQueuedMotionEvents = new LinkedList<>();
+    // Whether this controller is waiting for a state transition.
+    // Motion events will be queued and sent to listeners after the transition has taken place.
+    private boolean mStateChangeRequested = false;
 
     // The current state of the display.
     private int mState = STATE_CLEAR;
@@ -169,6 +176,14 @@
      * main thread.
      */
     void onMotionEvent(MotionEvent event) {
+        if (mStateChangeRequested) {
+            mQueuedMotionEvents.add(event);
+        } else {
+            sendEventToAllListeners(event);
+        }
+    }
+
+    private void sendEventToAllListeners(MotionEvent event) {
         final ArrayMap<Callback, Executor> entries;
         synchronized (mLock) {
             // callbacks may remove themselves. Perform a shallow copy to avoid concurrent
@@ -209,6 +224,10 @@
                 callback.onStateChanged(state);
             }
         }
+        mStateChangeRequested = false;
+        while (mQueuedMotionEvents.size() > 0) {
+            sendEventToAllListeners(mQueuedMotionEvents.poll());
+        }
     }
 
     /**
@@ -253,6 +272,7 @@
             } catch (RemoteException re) {
                 throw new RuntimeException(re);
             }
+            mStateChangeRequested = true;
         }
     }
 
@@ -281,6 +301,7 @@
             } catch (RemoteException re) {
                 throw new RuntimeException(re);
             }
+            mStateChangeRequested = true;
         }
     }
 
@@ -302,6 +323,7 @@
             } catch (RemoteException re) {
                 throw new RuntimeException(re);
             }
+            mStateChangeRequested = true;
         }
     }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fa48730..f3315a8 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2179,8 +2179,8 @@
     }
 
     @Override
-    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
-        getSystemService(PermissionManager.class).selfRevokePermissions(permissions);
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+        getSystemService(PermissionManager.class).revokeOwnPermissionsOnKill(permissions);
     }
 
     @Override
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index a9ec11e..0801b24 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -72,6 +72,7 @@
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
 import android.window.IWindowOrganizerController;
+import android.window.BackNavigationInfo;
 import android.window.SplashScreenView;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
@@ -346,7 +347,8 @@
     void setRunningRemoteTransitionDelegate(in IApplicationThread caller);
 
     /**
-     * Prepare the back preview in the server
+     * Prepare the back navigation in the server. This setups the leashed for sysui to animate
+     * the back gesture and returns the data needed for the animation.
      */
-    void startBackPreview(IRemoteAnimationRunner runner);
+    android.window.BackNavigationInfo startBackNavigation();
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 67c42f6..63c1fd8 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -124,8 +124,8 @@
 import android.media.soundtrigger.SoundTriggerManager;
 import android.media.tv.ITvInputManager;
 import android.media.tv.TvInputManager;
-import android.media.tv.interactive.ITvIAppManager;
-import android.media.tv.interactive.TvIAppManager;
+import android.media.tv.interactive.ITvInteractiveAppManager;
+import android.media.tv.interactive.TvInteractiveAppManager;
 import android.media.tv.tunerresourcemanager.ITunerResourceManager;
 import android.media.tv.tunerresourcemanager.TunerResourceManager;
 import android.nearby.NearbyFrameworkInitializer;
@@ -964,13 +964,16 @@
                     }
                 });
 
-        registerService(Context.TV_IAPP_SERVICE, TvIAppManager.class,
-                new CachedServiceFetcher<TvIAppManager>() {
+        registerService(Context.TV_INTERACTIVE_APP_SERVICE, TvInteractiveAppManager.class,
+                new CachedServiceFetcher<TvInteractiveAppManager>() {
             @Override
-            public TvIAppManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder iBinder = ServiceManager.getServiceOrThrow(Context.TV_IAPP_SERVICE);
-                ITvIAppManager service = ITvIAppManager.Stub.asInterface(iBinder);
-                return new TvIAppManager(service, ctx.getUserId());
+            public TvInteractiveAppManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
+                IBinder iBinder =
+                        ServiceManager.getServiceOrThrow(Context.TV_INTERACTIVE_APP_SERVICE);
+                ITvInteractiveAppManager service =
+                        ITvInteractiveAppManager.Stub.asInterface(iBinder);
+                return new TvInteractiveAppManager(service, ctx.getUserId());
             }});
 
         registerService(Context.TV_INPUT_SERVICE, TvInputManager.class,
@@ -1021,19 +1024,21 @@
             }});
 
         registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
-                new StaticServiceFetcher<PersistentDataBlockManager>() {
+                new CachedServiceFetcher<PersistentDataBlockManager>() {
             @Override
-            public PersistentDataBlockManager createService() throws ServiceNotFoundException {
+            public PersistentDataBlockManager createService(ContextImpl ctx)
+                    throws ServiceNotFoundException {
                 IBinder b = ServiceManager.getServiceOrThrow(Context.PERSISTENT_DATA_BLOCK_SERVICE);
                 IPersistentDataBlockService persistentDataBlockService =
                         IPersistentDataBlockService.Stub.asInterface(b);
                 if (persistentDataBlockService != null) {
-                    return new PersistentDataBlockManager(persistentDataBlockService);
+                    return new PersistentDataBlockManager(ctx, persistentDataBlockService);
                 } else {
                     // not supported
                     return null;
                 }
-            }});
+            }
+         });
 
         registerService(Context.OEM_LOCK_SERVICE, OemLockManager.class,
                 new StaticServiceFetcher<OemLockManager>() {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8415794..cefd25a 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3012,6 +3012,54 @@
             "android.app.extra.PROVISIONING_ROLE_HOLDER_CUSTOM_USER_CONSENT_INTENT";
 
     /**
+     * Activity action: attempts to establish network connection
+     *
+     * <p>This intent can be accompanied by any of the relevant provisioning extras related to
+     * network connectivity, such as:
+     * <ul>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_SSID}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_HIDDEN}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PASSWORD}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}</li>
+     *     <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_EAP_METHOD}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_PHASE2_AUTH}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_IDENTITY}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY}</li>
+     *     <li>{@code #EXTRA_PROVISIONING_WIFI_DOMAIN}</li>
+     * </ul>
+     *
+     * <p>If there are provisioning extras related to network connectivity, this activity
+     * attempts to connect to the specified network. Otherwise it prompts the end-user to connect.
+     *
+     * <p>This activity is meant to be started by the provisioning initiator prior to starting
+     * {@link #ACTION_PROVISION_MANAGED_PROFILE} or {@link
+     * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}.
+     *
+     * <p>Note that network connectivity is still also handled when provisioning via {@link
+     * #ACTION_PROVISION_MANAGED_PROFILE} or {@link
+     * #ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE}. {@link
+     * #ACTION_ESTABLISH_NETWORK_CONNECTION} should only be used in cases when the provisioning
+     * initiator would like to do some additional logic after the network connectivity step and
+     * before the start of provisioning.
+     *
+     * If network connection is established, {@link Activity#RESULT_OK} will be returned. Otherwise
+     * the result will be {@link Activity#RESULT_CANCELED}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.DISPATCH_PROVISIONING_MESSAGE)
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SystemApi
+    public static final String ACTION_ESTABLISH_NETWORK_CONNECTION =
+            "android.app.action.ESTABLISH_NETWORK_CONNECTION";
+
+    /**
      * Maximum supported password length. Kind-of arbitrary.
      * @hide
      */
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 8ab6688..a97b7b3 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -16,7 +16,6 @@
 
 package android.companion.virtual;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -43,10 +42,6 @@
 import android.os.ResultReceiver;
 import android.view.Surface;
 
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
 import java.util.concurrent.Executor;
 
 /**
@@ -61,23 +56,6 @@
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "VirtualDeviceManager";
 
-    /** @hide */
-    @IntDef(prefix = "DISPLAY_FLAG_",
-            flag = true,
-            value = {DISPLAY_FLAG_TRUSTED})
-    @Retention(RetentionPolicy.SOURCE)
-    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
-    public @interface DisplayFlags {}
-
-    /**
-     * Indicates that the display is trusted to show system decorations and receive inputs without
-     * users' touch.
-     *
-     * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED
-     * @hide  // TODO(b/194949534): Unhide this API
-     */
-    public static final int DISPLAY_FLAG_TRUSTED = 1;
-
     private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS =
             DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC
                     | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT
@@ -102,12 +80,12 @@
      * @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
      *   Companion Device Manager. Virtual devices must have a corresponding association with CDM in
      *   order to be created.
-     * @hide
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
     @Nullable
-    public VirtualDevice createVirtualDevice(int associationId, VirtualDeviceParams params) {
-        // TODO(b/194949534): Unhide this API
+    public VirtualDevice createVirtualDevice(
+            int associationId,
+            @NonNull VirtualDeviceParams params) {
         try {
             IVirtualDevice virtualDevice = mService.createVirtualDevice(
                     new Binder(), mContext.getPackageName(), associationId, params);
@@ -187,7 +165,12 @@
          * @param surface The surface to which the content of the virtual display should
          * be rendered, or null if there is none initially. The surface can also be set later using
          * {@link VirtualDisplay#setSurface(Surface)}.
-         * @param flags Either 0, or {@link #DISPLAY_FLAG_TRUSTED}.
+         * @param flags A combination of virtual display flags accepted by
+         * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
+         * automatically set for all virtual devices:
+         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
+         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
+         * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @param handler The handler on which the listener should be invoked, or null
          * if the listener should be invoked on the calling thread's looper.
@@ -195,9 +178,7 @@
          * not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
-         * @hide
          */
-        // TODO(b/194949534): Unhide this API
         // Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a
         // handler
         @SuppressLint("ExecutorRegistration")
@@ -207,7 +188,7 @@
                 int height,
                 int densityDpi,
                 @Nullable Surface surface,
-                @DisplayFlags int flags,
+                int flags,
                 @Nullable Handler handler,
                 @Nullable VirtualDisplay.Callback callback) {
             // TODO(b/205343547): Handle display groups properly instead of creating a new display
@@ -246,7 +227,6 @@
          * @param inputDeviceName the name to call this input device
          * @param vendorId the vendor id
          * @param productId the product id
-         * @hide
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -273,7 +253,6 @@
          * @param inputDeviceName the name to call this input device
          * @param vendorId the vendor id
          * @param productId the product id
-         * @hide
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -300,7 +279,6 @@
          * @param inputDeviceName the name to call this input device
          * @param vendorId the vendor id
          * @param productId the product id
-         * @hide
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -328,12 +306,8 @@
          * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
          * be added by DisplayManagerService.
          */
-        private int getVirtualDisplayFlags(@DisplayFlags int flags) {
-            int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS;
-            if ((flags & DISPLAY_FLAG_TRUSTED) != 0) {
-                virtualDisplayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
-            }
-            return virtualDisplayFlags;
+        private int getVirtualDisplayFlags(int flags) {
+            return DEFAULT_VIRTUAL_DISPLAY_FLAGS | flags;
         }
 
         private String getVirtualDisplayName() {
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 169f4e1..2ddfeb4 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -22,6 +22,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -41,7 +42,7 @@
  *
  * @hide
  */
-// TODO(b/194949534): Unhide this API
+@SystemApi
 public final class VirtualDeviceParams implements Parcelable {
 
     /** @hide */
@@ -53,15 +54,11 @@
 
     /**
      * Indicates that the lock state of the virtual device should be always locked.
-     *
-     * @hide  // TODO(b/194949534): Unhide this API
      */
     public static final int LOCK_STATE_ALWAYS_LOCKED = 0;
 
     /**
      * Indicates that the lock state of the virtual device should be always unlocked.
-     *
-     * @hide  // TODO(b/194949534): Unhide this API
      */
     public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
 
@@ -112,6 +109,7 @@
      * Returns the set of activities allowed to be streamed, or {@code null} if this is not set.
      *
      * @see Builder#setAllowedActivities(Set)
+     * @hide  // TODO(b/194949534): Unhide this API
      */
     @Nullable
     public Set<ComponentName> getAllowedActivities() {
@@ -126,6 +124,7 @@
      * set.
      *
      * @see Builder#setBlockedActivities(Set)
+     * @hide  // TODO(b/194949534): Unhide this API
      */
     @Nullable
     public Set<ComponentName> getBlockedActivities() {
@@ -169,6 +168,7 @@
     }
 
     @Override
+    @NonNull
     public String toString() {
         return "VirtualDeviceParams("
                 + " mLockState=" + mLockState
@@ -178,6 +178,7 @@
                 + ")";
     }
 
+    @NonNull
     public static final Parcelable.Creator<VirtualDeviceParams> CREATOR =
             new Parcelable.Creator<VirtualDeviceParams>() {
                 public VirtualDeviceParams createFromParcel(Parcel in) {
@@ -216,13 +217,25 @@
 
         /**
          * Sets the user handles with matching managed accounts on the remote device to which
-         * this virtual device is streaming.
+         * this virtual device is streaming. The caller is responsible for verifying the presence
+         * and legitimacy of a matching managed account on the remote device.
+         *
+         * <p>If the app streaming policy is
+         * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
+         * NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY}, activities not in
+         * {@code usersWithMatchingAccounts} will be blocked from starting.
+         *
+         * <p> If {@code usersWithMatchingAccounts} is empty (the default), streaming is allowed
+         * only if there is no device policy, or if the nearby streaming policy is
+         * {@link android.app.admin.DevicePolicyManager#NEARBY_STREAMING_ENABLED
+         * NEARBY_STREAMING_ENABLED}.
          *
          * @param usersWithMatchingAccounts A set of user handles with matching managed
          *   accounts on the remote device this is streaming to.
          *
          * @see android.app.admin.DevicePolicyManager#NEARBY_STREAMING_SAME_MANAGED_ACCOUNT_ONLY
          */
+        @NonNull
         public Builder setUsersWithMatchingAccounts(
                 @NonNull Set<UserHandle> usersWithMatchingAccounts) {
             mUsersWithMatchingAccounts = usersWithMatchingAccounts;
@@ -242,6 +255,7 @@
          *
          * @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
          *   in the virtual device.
+         * @hide  // TODO(b/194949534): Unhide this API
          */
         public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
             if (mBlockedActivities != null && allowedActivities != null) {
@@ -265,6 +279,7 @@
          *
          * @param blockedActivities A set of {@link ComponentName} to be blocked launching from
          *   virtual device.
+         * @hide  // TODO(b/194949534): Unhide this API
          */
         public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
             if (mAllowedActivities != null && blockedActivities != null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2309fb6..845d23c 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3829,7 +3829,7 @@
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
-            TV_IAPP_SERVICE,
+            TV_INTERACTIVE_APP_SERVICE,
             TV_INPUT_SERVICE,
             //@hide: TV_TUNER_RESOURCE_MGR_SERVICE,
             //@hide: NETWORK_SCORE_SERVICE,
@@ -5356,13 +5356,13 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
-     * {@link android.media.tv.interactive.TvIAppManager} for interacting with TV interactive
-     * applications (TV iApp) on the device.
+     * {@link android.media.tv.interactive.TvInteractiveAppManager} for interacting with TV
+     * interactive applications on the device.
      *
      * @see #getSystemService(String)
-     * @see android.media.tv.interactive.TvIAppManager
+     * @see android.media.tv.interactive.TvInteractiveAppManager
      */
-    public static final String TV_IAPP_SERVICE = "tv_iapp";
+    public static final String TV_INTERACTIVE_APP_SERVICE = "tv_interactive_app";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve a
@@ -6417,10 +6417,10 @@
      * Triggers the asynchronous revocation of a permission.
      *
      * @param permName The name of the permission to be revoked.
-     * @see #selfRevokePermissions(Collection)
+     * @see #revokeOwnPermissionsOnKill(Collection)
      */
-    public void selfRevokePermission(@NonNull String permName) {
-        selfRevokePermissions(Collections.singletonList(permName));
+    public void revokeOwnPermissionOnKill(@NonNull String permName) {
+        revokeOwnPermissionsOnKill(Collections.singletonList(permName));
     }
 
     /**
@@ -6445,7 +6445,7 @@
      * @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)
      * @see PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)
      */
-    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 805e499..6ae768a 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -1016,8 +1016,8 @@
     }
 
     @Override
-    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
-        mBase.selfRevokePermissions(permissions);
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
+        mBase.revokeOwnPermissionsOnKill(permissions);
     }
 
     @Override
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index 4683d25..acceb65 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -110,9 +110,9 @@
     @Retention(RetentionPolicy.SOURCE)
     @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN,
             USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE,
-            USAGE_GPU_COLOR_OUTPUT, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE,
-            USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP,
-            USAGE_GPU_MIPMAP_COMPLETE})
+            USAGE_GPU_COLOR_OUTPUT, USAGE_COMPOSER_OVERLAY, USAGE_PROTECTED_CONTENT,
+            USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA,
+            USAGE_GPU_CUBE_MAP, USAGE_GPU_MIPMAP_COMPLETE, USAGE_FRONT_BUFFER})
     public @interface Usage {};
 
     @Usage
@@ -151,6 +151,12 @@
     public static final long USAGE_GPU_CUBE_MAP           = 1 << 25;
     /** Usage: The buffer contains a complete mipmap hierarchy */
     public static final long USAGE_GPU_MIPMAP_COMPLETE    = 1 << 26;
+    /** Usage: The buffer is used for front-buffer rendering. When front-buffering rendering is
+     * specified, different usages may adjust their behavior as a result. For example, when
+     * used as USAGE_GPU_COLOR_OUTPUT the buffer will behave similar to a single-buffered window.
+     * When used with USAGE_COMPOSER_OVERLAY, the system will try to prioritize the buffer
+     * receiving an overlay plane & avoid caching it in intermediate composition buffers. */
+    public static final long USAGE_FRONT_BUFFER           = 1 << 32;
 
     /**
      * Creates a new <code>HardwareBuffer</code> instance.
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 89ac8bf..eefa1d3 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -334,6 +334,7 @@
      * @hide
      */
     @TestApi
+    @SystemApi
     public static final int VIRTUAL_DISPLAY_FLAG_TRUSTED = 1 << 10;
 
     /**
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3bc3ec8..e8b3ae9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3711,10 +3711,10 @@
         final int m = list.size();
         int i = 0;
         for (; i < m && i < n; i++) {
-            list.set(i, (T) readParcelableInternal(cl, clazz));
+            list.set(i, readParcelableInternal(cl, clazz));
         }
         for (; i < n; i++) {
-            list.add((T) readParcelableInternal(cl, clazz));
+            list.add(readParcelableInternal(cl, clazz));
         }
         for (; i < m; i++) {
             list.remove(n);
@@ -4217,7 +4217,8 @@
      * trying to instantiate an element.
      */
     @Nullable
-    public <T> T readParcelable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+    public <T extends Parcelable> T readParcelable(@Nullable ClassLoader loader,
+            @NonNull Class<? super T> clazz) {
         Objects.requireNonNull(clazz);
         return readParcelableInternal(loader, clazz);
     }
@@ -4227,7 +4228,8 @@
      */
     @SuppressWarnings("unchecked")
     @Nullable
-    private <T> T readParcelableInternal(@Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+    private <T extends Parcelable> T readParcelableInternal(@Nullable ClassLoader loader,
+            @Nullable Class<? super T> clazz) {
         Parcelable.Creator<?> creator = readParcelableCreatorInternal(loader, clazz);
         if (creator == null) {
             return null;
@@ -4463,7 +4465,8 @@
      * deserializing the object.
      */
     @Nullable
-    public <T> T readSerializable(@Nullable ClassLoader loader, @NonNull Class<T> clazz) {
+    public <T extends Serializable> T readSerializable(@Nullable ClassLoader loader,
+            @NonNull Class<? super T> clazz) {
         Objects.requireNonNull(clazz);
         return readSerializableInternal(
                 loader == null ? getClass().getClassLoader() : loader, clazz);
@@ -4473,8 +4476,8 @@
      * @param clazz The type of the serializable expected or {@code null} for performing no checks
      */
     @Nullable
-    private <T> T readSerializableInternal(@Nullable final ClassLoader loader,
-            @Nullable Class<T> clazz) {
+    private <T extends Serializable> T readSerializableInternal(@Nullable final ClassLoader loader,
+            @Nullable Class<? super T> clazz) {
         String name = readString();
         if (name == null) {
             // For some reason we were unable to read the name of the Serializable (either there
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index a63f68a..2fe0622 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -280,6 +280,26 @@
     public static final int LAST_APPLICATION_UID = 19999;
 
     /**
+     * Defines the start of a range of UIDs going from this number to
+     * {@link #LAST_SUPPLEMENTAL_UID} that are reserved for assigning to
+     * supplemental processes. There is a 1-1 mapping between a supplemental
+     * process UID and the app that it belongs to, which can be computed by
+     * subtracting (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID) from the
+     * uid of a supplemental process.
+     *
+     * Note that there are no GIDs associated with these processes; storage
+     * attribution for them will be done using project IDs.
+     * @hide
+     */
+    public static final int FIRST_SUPPLEMENTAL_UID = 20000;
+
+    /**
+     * Last UID that is used for supplemental processes.
+     * @hide
+     */
+    public static final int LAST_SUPPLEMENTAL_UID = 29999;
+
+    /**
      * First uid used for fully isolated sandboxed processes spawned from an app zygote
      * @hide
      */
@@ -881,6 +901,46 @@
     }
 
     /**
+     * Returns whether the provided UID belongs to a supplemental process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final boolean isSupplemental(int uid) {
+        uid = UserHandle.getAppId(uid);
+        return (uid >= FIRST_SUPPLEMENTAL_UID && uid <= LAST_SUPPLEMENTAL_UID);
+    }
+
+    /**
+     *
+     * Returns the app process corresponding to a supplemental process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int toAppUid(int uid) {
+        return uid - (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
+     *
+     * Returns the supplemental process corresponding to an app process.
+     *
+     * @hide
+     */
+    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int toSupplementalUid(int uid) {
+        return uid + (FIRST_SUPPLEMENTAL_UID - FIRST_APPLICATION_UID);
+    }
+
+    /**
+     * Returns whether the current process is a supplemental process.
+     */
+    public static final boolean isSupplemental() {
+        return isSupplemental(myUid());
+    }
+
+    /**
      * Returns the UID assigned to a particular user name, or -1 if there is
      * none.  If the given string consists of only numbers, it is converted
      * directly to a uid.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 29accb9..8df659d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -80,6 +80,7 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.sysprop.VoldProperties;
@@ -1443,28 +1444,39 @@
      *
      * @hide
      */
-    public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+    public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+    /** {@hide} */
+    @TestApi
+    public static final String
+            STORAGE_THRESHOLD_PERCENT_HIGH_KEY = "storage_threshold_percent_high";
     /**
      * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
-     * in low free space category.
+     * in low free space category and can be configured via
+     * Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE.
      *
      * @hide
      */
-    public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5;
+    public static final int DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW = 5;
     /**
      * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
      * allocated for cache.
      *
      * @hide
      */
-    public static final int CACHE_RESERVE_PERCENT_HIGH = 10;
+    public static final int DEFAULT_CACHE_RESERVE_PERCENT_HIGH = 10;
+    /** {@hide} */
+    @TestApi
+    public static final String CACHE_RESERVE_PERCENT_HIGH_KEY = "cache_reserve_percent_high";
     /**
      * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
      * allocated for cache.
      *
      * @hide
      */
-    public static final int CACHE_RESERVE_PERCENT_LOW = 2;
+    public static final int DEFAULT_CACHE_RESERVE_PERCENT_LOW = 2;
+    /** {@hide} */
+    @TestApi
+    public static final String CACHE_RESERVE_PERCENT_LOW_KEY = "cache_reserve_percent_low";
 
     private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
 
@@ -1490,7 +1502,8 @@
     @UnsupportedAppUsage
     public long getStorageLowBytes(File path) {
         final long lowPercent = Settings.Global.getInt(mResolver,
-                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW);
+                Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE,
+                DEFAULT_STORAGE_THRESHOLD_PERCENT_LOW);
         final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
 
         final long maxLowBytes = Settings.Global.getLong(mResolver,
@@ -1510,24 +1523,33 @@
     @TestApi
     @SuppressLint("StreamFiles")
     public long computeStorageCacheBytes(@NonNull File path) {
+        final int storageThresholdPercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                STORAGE_THRESHOLD_PERCENT_HIGH_KEY, DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+        final int cacheReservePercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                CACHE_RESERVE_PERCENT_HIGH_KEY, DEFAULT_CACHE_RESERVE_PERCENT_HIGH);
+        final int cacheReservePercentLow = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                CACHE_RESERVE_PERCENT_LOW_KEY, DEFAULT_CACHE_RESERVE_PERCENT_LOW);
         final long totalBytes = path.getTotalSpace();
         final long usableBytes = path.getUsableSpace();
-        final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100;
+        final long storageThresholdHighBytes = totalBytes * storageThresholdPercentHigh / 100;
         final long storageThresholdLowBytes = getStorageLowBytes(path);
         long result;
         if (usableBytes > storageThresholdHighBytes) {
-            // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space,
-            // reserve CACHE_RESERVE_PERCENT_HIGH of total space
-            result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100;
+            // If free space is >storageThresholdPercentHigh of total space,
+            // reserve cacheReservePercentHigh of total space
+            result = totalBytes * cacheReservePercentHigh / 100;
         } else if (usableBytes < storageThresholdLowBytes) {
-            // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB),
-            // reserve CACHE_RESERVE_PERCENT_LOW of total space
-            result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
+            // If free space is <min(storageThresholdPercentLow of total space, 500MB),
+            // reserve cacheReservePercentLow of total space
+            result = totalBytes * cacheReservePercentLow / 100;
         } else {
             // Else, linearly interpolate the amount of space to reserve
-            double slope = (CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW) * totalBytes
+            double slope = (cacheReservePercentHigh - cacheReservePercentLow) * totalBytes
                     / (100.0 * (storageThresholdHighBytes - storageThresholdLowBytes));
-            double intercept = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100.0
+            double intercept = totalBytes * cacheReservePercentLow / 100.0
                     - storageThresholdLowBytes * slope;
             result = Math.round(slope * usableBytes + intercept);
         }
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index 5814bac..0894e37 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -56,6 +56,6 @@
             in AndroidFuture<String> callback);
     void getUnusedAppCount(
             in AndroidFuture callback);
-    void selfRevokePermissions(in String packageName, in List<String> permissions,
+    void revokeOwnPermissionsOnKill(in String packageName, in List<String> permissions,
             in AndroidFuture callback);
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 8e5581b..1c0320e 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -76,7 +76,7 @@
 
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
-    void selfRevokePermissions(String packageName, in List<String> permissions);
+    void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions);
 
     void startOneTimePermissionSession(String packageName, int userId, long timeout,
             int importanceToResetTimer, int importanceToKeepSessionAlive);
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 47cd107..8733ac4 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -836,15 +836,15 @@
      * @param packageName The name of the package for which the permissions will be revoked.
      * @param permissions List of permissions to be revoked.
      *
-     * @see Context#selfRevokePermissions(Collection)
+     * @see Context#revokeOwnPermissionsOnKill(Collection)
      *
      * @hide
      */
-    public void selfRevokePermissions(@NonNull String packageName,
+    public void revokeOwnPermissionsOnKill(@NonNull String packageName,
             @NonNull List<String> permissions) {
         mRemoteService.postAsync(service -> {
             AndroidFuture<Void> future = new AndroidFuture<>();
-            service.selfRevokePermissions(packageName, permissions, future);
+            service.revokeOwnPermissionsOnKill(packageName, permissions, future);
             return future;
         }).whenComplete((result, err) -> {
             if (err != null) {
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index dcbab62..b1e3cfc 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -337,10 +337,10 @@
      * @param permissions List of permissions to be revoked.
      * @param callback Callback waiting for operation to be complete.
      *
-     * @see PermissionManager#selfRevokePermissions(java.util.Collection)
+     * @see PermissionManager#revokeOwnPermissionsOnKill(java.util.Collection)
      */
     @BinderThread
-    public void onSelfRevokePermissions(@NonNull String packageName,
+    public void onRevokeOwnPermissionsOnKill(@NonNull String packageName,
             @NonNull List<String> permissions, @NonNull Runnable callback) {
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
@@ -669,13 +669,13 @@
             }
 
             @Override
-            public void selfRevokePermissions(@NonNull String packageName,
+            public void revokeOwnPermissionsOnKill(@NonNull String packageName,
                     @NonNull List<String> permissions, @NonNull AndroidFuture callback) {
                 try {
                     enforceSomePermissionsGrantedToCaller(
                             Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
                     Objects.requireNonNull(callback);
-                    onSelfRevokePermissions(packageName, permissions,
+                    onRevokeOwnPermissionsOnKill(packageName, permissions,
                             () -> callback.complete(null));
                 } catch (Throwable t) {
                     callback.completeExceptionally(t);
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 13941dc..e4aee76 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -562,12 +562,12 @@
     }
 
     /**
-     * @see Context#selfRevokePermissions(Collection)
+     * @see Context#revokeOwnPermissionsOnKill(Collection)
      * @hide
      */
-    public void selfRevokePermissions(@NonNull Collection<String> permissions) {
+    public void revokeOwnPermissionsOnKill(@NonNull Collection<String> permissions) {
         try {
-            mPermissionManager.selfRevokePermissions(mContext.getPackageName(),
+            mPermissionManager.revokeOwnPermissionsOnKill(mContext.getPackageName(),
                     new ArrayList<String>(permissions));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e568370..be2e3b2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9063,6 +9063,16 @@
         public static final String SCREENSAVER_DEFAULT_COMPONENT = "screensaver_default_component";
 
         /**
+         * The complications that are enabled to be shown over the screensaver by the user. Holds
+         * a comma separated list of
+         * {@link com.android.settingslib.dream.DreamBackend.ComplicationType}.
+         *
+         * @hide
+         */
+        public static final String SCREENSAVER_ENABLED_COMPLICATIONS =
+                "screensaver_enabled_complications";
+
+        /**
          * The default NFC payment component
          * @hide
          */
@@ -12823,16 +12833,6 @@
                 SYS_STORAGE_CACHE_PERCENTAGE = "sys_storage_cache_percentage";
 
         /**
-         * Maximum bytes of storage on the device that is reserved for cached
-         * data.
-         *
-         * @hide
-         */
-        @Readable
-        public static final String
-                SYS_STORAGE_CACHE_MAX_BYTES = "sys_storage_cache_max_bytes";
-
-        /**
          * The maximum reconnect delay for short network outages or when the
          * network is suspended due to phone use.
          *
diff --git a/core/java/android/service/persistentdata/PersistentDataBlockManager.java b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
index 8242f4e..44a8862 100644
--- a/core/java/android/service/persistentdata/PersistentDataBlockManager.java
+++ b/core/java/android/service/persistentdata/PersistentDataBlockManager.java
@@ -17,6 +17,7 @@
 package android.service.persistentdata;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
@@ -25,6 +26,8 @@
 import android.os.RemoteException;
 import android.service.oemlock.OemLockManager;
 
+import com.android.internal.R;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -50,6 +53,7 @@
 @SystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE)
 public class PersistentDataBlockManager {
     private static final String TAG = PersistentDataBlockManager.class.getSimpleName();
+    private final Context mContext;
     private IPersistentDataBlockService sService;
 
     /**
@@ -74,7 +78,10 @@
     public @interface FlashLockState {}
 
     /** @hide */
-    public PersistentDataBlockManager(IPersistentDataBlockService service) {
+    public PersistentDataBlockManager(
+            Context context,
+            IPersistentDataBlockService service) {
+        mContext = context;
         sService = service;
     }
 
@@ -204,4 +211,15 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Returns the package name which can access the persistent data partition.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    public String getPersistentDataPackageName() {
+        return mContext.getString(R.string.config_persistentDataPackageName);
+    }
 }
diff --git a/core/java/android/service/security/attestationverification/OWNERS b/core/java/android/service/security/attestationverification/OWNERS
new file mode 100644
index 0000000..12c9978
--- /dev/null
+++ b/core/java/android/service/security/attestationverification/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/base:/core/java/android/security/attestationverification/OWNERS
diff --git a/core/java/android/service/smartspace/SmartspaceService.java b/core/java/android/service/smartspace/SmartspaceService.java
index 7dd85cc..b903fbe 100644
--- a/core/java/android/service/smartspace/SmartspaceService.java
+++ b/core/java/android/service/smartspace/SmartspaceService.java
@@ -245,7 +245,9 @@
     public abstract void onDestroySmartspaceSession(@NonNull SmartspaceSessionId sessionId);
 
     private void doDestroy(@NonNull SmartspaceSessionId sessionId) {
-        Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+        if (DEBUG) {
+            Log.d(TAG, "doDestroy mSessionCallbacks: " + mSessionCallbacks);
+        }
         super.onDestroy();
         mSessionCallbacks.remove(sessionId);
         onDestroySmartspaceSession(sessionId);
diff --git a/core/java/android/window/BackNavigationInfo.aidl b/core/java/android/window/BackNavigationInfo.aidl
new file mode 100644
index 0000000..1529902
--- /dev/null
+++ b/core/java/android/window/BackNavigationInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.window;
+
+/**
+ * @hide
+ */
+parcelable BackNavigationInfo;
\ No newline at end of file
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
new file mode 100644
index 0000000..571714c
--- /dev/null
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -0,0 +1,242 @@
+/*
+ * 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.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.hardware.HardwareBuffer;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteCallback;
+import android.view.SurfaceControl;
+
+/**
+ * Information to be sent to SysUI about a back event.
+ *
+ * @hide
+ */
+public final class BackNavigationInfo implements Parcelable {
+
+    /**
+     * The target of the back navigation is undefined.
+     */
+    public static final int TYPE_UNDEFINED = -1;
+
+    /**
+     * Navigating back will close the currently visible dialog
+     */
+    public static final int TYPE_DIALOG_CLOSE = 0;
+
+    /**
+     * Navigating back will bring the user back to the home screen
+     */
+    public static final int TYPE_RETURN_TO_HOME = 1;
+
+    /**
+     * Navigating back will bring the user to the previous activity in the same Task
+     */
+    public static final int TYPE_CROSS_ACTIVITY = 2;
+
+    /**
+     * Navigating back will bring the user to the previous activity in the previous Task
+     */
+    public static final int TYPE_CROSS_TASK = 3;
+
+    /**
+     * Defines the type of back destinations a back even can lead to. This is used to define the
+     * type of animation that need to be run on SystemUI.
+     */
+    @IntDef(prefix = "TYPE_", value = {
+            TYPE_UNDEFINED,
+            TYPE_DIALOG_CLOSE,
+            TYPE_RETURN_TO_HOME,
+            TYPE_CROSS_ACTIVITY,
+            TYPE_CROSS_TASK})
+    @interface BackTargetType {
+    }
+
+    private final int mType;
+    @Nullable
+    private final SurfaceControl mDepartingWindowContainer;
+    @Nullable
+    private final SurfaceControl mScreenshotSurface;
+    @Nullable
+    private final HardwareBuffer mScreenshotBuffer;
+    @Nullable
+    private final RemoteCallback mRemoteCallback;
+    @Nullable
+    private final WindowConfiguration mTaskWindowConfiguration;
+
+    /**
+     * Create a new {@link BackNavigationInfo} instance.
+     *
+     * @param type  The {@link BackTargetType} of the destination (what will be displayed after
+     *              the back action)
+     * @param topWindowLeash      The leash to animate away the current topWindow. The consumer
+     *                            of the leash is responsible for removing it.
+     * @param screenshotSurface The screenshot of the previous activity to be displayed.
+     * @param screenshotBuffer      A buffer containing a screenshot used to display the activity.
+     *                            See {@link  #getScreenshotHardwareBuffer()} for information
+     *                            about nullity.
+     * @param taskWindowConfiguration The window configuration of the Task being animated
+     *                            beneath.
+     * @param onBackNavigationDone   The callback to be called once the client is done with the back
+     *                           preview.
+     */
+    public BackNavigationInfo(@BackTargetType int type,
+            @Nullable SurfaceControl topWindowLeash,
+            @Nullable SurfaceControl screenshotSurface,
+            @Nullable HardwareBuffer screenshotBuffer,
+            @Nullable WindowConfiguration taskWindowConfiguration,
+            @NonNull RemoteCallback onBackNavigationDone) {
+        mType = type;
+        mDepartingWindowContainer = topWindowLeash;
+        mScreenshotSurface = screenshotSurface;
+        mScreenshotBuffer = screenshotBuffer;
+        mTaskWindowConfiguration = taskWindowConfiguration;
+        mRemoteCallback = onBackNavigationDone;
+    }
+
+    private BackNavigationInfo(@NonNull Parcel in) {
+        mType = in.readInt();
+        mDepartingWindowContainer = in.readTypedObject(SurfaceControl.CREATOR);
+        mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR);
+        mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR);
+        mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
+        mRemoteCallback = requireNonNull(in.readTypedObject(RemoteCallback.CREATOR));
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeTypedObject(mDepartingWindowContainer, flags);
+        dest.writeTypedObject(mScreenshotSurface, flags);
+        dest.writeTypedObject(mScreenshotBuffer, flags);
+        dest.writeTypedObject(mTaskWindowConfiguration, flags);
+        dest.writeTypedObject(mRemoteCallback, flags);
+    }
+
+    /**
+     * Returns the type of back navigation that is about to happen.
+     * @see BackTargetType
+     */
+    public @BackTargetType int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns a leash to the top window container that needs to be animated. This can be null if
+     * the back animation is controlled by the application.
+     */
+    @Nullable
+    public SurfaceControl getDepartingWindowContainer() {
+        return mDepartingWindowContainer;
+    }
+
+    /**
+     *  Returns the {@link SurfaceControl} that should be used to display a screenshot of the
+     *  previous activity.
+     */
+    @Nullable
+    public SurfaceControl getScreenshotSurface() {
+        return mScreenshotSurface;
+    }
+
+    /**
+     * Returns the {@link HardwareBuffer} containing the screenshot the activity about to be
+     * shown. This can be null if one of the following conditions is met:
+     * <ul>
+     *     <li>The screenshot is not available
+     *     <li> The previous activity is the home screen ( {@link  #TYPE_RETURN_TO_HOME}
+     *     <li> The current window is a dialog ({@link  #TYPE_DIALOG_CLOSE}
+     *     <li> The back animation is controlled by the application
+     * </ul>
+     */
+    @Nullable
+    public HardwareBuffer getScreenshotHardwareBuffer() {
+        return mScreenshotBuffer;
+    }
+
+    /**
+     * Returns the {@link WindowConfiguration} of the current task. This is null when the top
+     * application is controlling the back animation.
+     */
+    @Nullable
+    public WindowConfiguration getTaskWindowConfiguration() {
+        return mTaskWindowConfiguration;
+    }
+
+    /**
+     * Callback to be called when the back preview is finished in order to notify the server that
+     * it can clean up the resources created for the animation.
+     */
+    public void onBackNavigationFinished() {
+        mRemoteCallback.sendResult(null);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<BackNavigationInfo> CREATOR = new Creator<BackNavigationInfo>() {
+        @Override
+        public BackNavigationInfo createFromParcel(Parcel in) {
+            return new BackNavigationInfo(in);
+        }
+
+        @Override
+        public BackNavigationInfo[] newArray(int size) {
+            return new BackNavigationInfo[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        return "BackNavigationInfo{"
+                + "mType=" + typeToString(mType) + " (" + mType + ")"
+                + ", mDepartingWindowContainer=" + mDepartingWindowContainer
+                + ", mScreenshotSurface=" + mScreenshotSurface
+                + ", mTaskWindowConfiguration= " + mTaskWindowConfiguration
+                + ", mScreenshotBuffer=" + mScreenshotBuffer
+                + ", mRemoteCallback=" + mRemoteCallback
+                + '}';
+    }
+
+    /**
+     * Translates the {@link BackNavigationInfo} integer type to its String representation
+     */
+    public static String typeToString(@BackTargetType int type) {
+        switch (type) {
+            case  TYPE_UNDEFINED:
+                return "TYPE_UNDEFINED";
+            case TYPE_DIALOG_CLOSE:
+                return "TYPE_DIALOG_CLOSE";
+            case TYPE_RETURN_TO_HOME:
+                return "TYPE_RETURN_TO_HOME";
+            case TYPE_CROSS_ACTIVITY:
+                return "TYPE_CROSS_ACTIVITY";
+            case TYPE_CROSS_TASK:
+                return "TYPE_CROSS_TASK";
+        }
+        return String.valueOf(type);
+    }
+}
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
index 62517fa..bf23ad1 100644
--- a/core/java/com/android/internal/midi/MidiFramer.java
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -99,6 +99,12 @@
                 }
             } else { // data byte
                 if (!mInSysEx) {
+                    // Hack to avoid crashing if we start parsing in the middle
+                    // of a data stream
+                    if (mNeeded <= 0) {
+                        break;
+                    }
+
                     mBuffer[mCount++] = currentByte;
                     if (--mNeeded == 0) {
                         if (mRunningStatus != 0) {
diff --git a/core/java/com/android/internal/midi/OWNERS b/core/java/com/android/internal/midi/OWNERS
new file mode 100644
index 0000000..af273a6
--- /dev/null
+++ b/core/java/com/android/internal/midi/OWNERS
@@ -0,0 +1 @@
+include /services/midi/OWNERS
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 451eec0..7c203fb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -12758,6 +12758,8 @@
 
             SparseLongArray rxPackets = new SparseLongArray();
             SparseLongArray txPackets = new SparseLongArray();
+            SparseLongArray rxTimesMs = new SparseLongArray();
+            SparseLongArray txTimesMs = new SparseLongArray();
             long totalTxPackets = 0;
             long totalRxPackets = 0;
             if (delta != null) {
@@ -12791,7 +12793,7 @@
                         mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
                                 entry.rxPackets);
 
-                        add(rxPackets, uid, entry.rxPackets);
+                        rxPackets.incrementValue(uid, entry.rxPackets);
 
                         // Sum the total number of packets so that the Rx Power can
                         // be evenly distributed amongst the apps.
@@ -12810,7 +12812,7 @@
                         mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
                                 entry.txPackets);
 
-                        add(txPackets, uid, entry.txPackets);
+                        txPackets.incrementValue(uid, entry.txPackets);
 
                         // Sum the total number of packets so that the Tx Power can
                         // be evenly distributed amongst the apps.
@@ -12934,12 +12936,9 @@
                                     + scanTxTimeSinceMarkMs + " ms)");
                         }
 
-                        ControllerActivityCounterImpl activityCounter =
-                                uid.getOrCreateWifiControllerActivityLocked();
-                        activityCounter.getOrCreateRxTimeCounter()
-                                .increment(scanRxTimeSinceMarkMs, elapsedRealtimeMs);
-                        activityCounter.getOrCreateTxTimeCounters()[0]
-                                .increment(scanTxTimeSinceMarkMs, elapsedRealtimeMs);
+                        rxTimesMs.incrementValue(uid.getUid(), scanRxTimeSinceMarkMs);
+                        txTimesMs.incrementValue(uid.getUid(), scanTxTimeSinceMarkMs);
+
                         leftOverRxTimeMs -= scanRxTimeSinceMarkMs;
                         leftOverTxTimeMs -= scanTxTimeSinceMarkMs;
                     }
@@ -12978,36 +12977,51 @@
                 // Distribute the remaining Tx power appropriately between all apps that transmitted
                 // packets.
                 for (int i = 0; i < txPackets.size(); i++) {
-                    final Uid uid = getUidStatsLocked(txPackets.keyAt(i),
-                            elapsedRealtimeMs, uptimeMs);
+                    final int uid = txPackets.keyAt(i);
                     final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
                             / totalTxPackets;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "  TxTime for UID " + uid.getUid() + ": " + myTxTimeMs + " ms");
-                    }
-                    uid.getOrCreateWifiControllerActivityLocked().getOrCreateTxTimeCounters()[0]
-                            .increment(myTxTimeMs, elapsedRealtimeMs);
-                    if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.incrementValue(uid.getUid(),
-                                mWifiPowerCalculator.calcPowerFromControllerDataMah(
-                                        0, myTxTimeMs, 0));
-                    }
+                    txTimesMs.incrementValue(uid, myTxTimeMs);
                 }
 
                 // Distribute the remaining Rx power appropriately between all apps that received
                 // packets.
                 for (int i = 0; i < rxPackets.size(); i++) {
-                    final Uid uid = getUidStatsLocked(rxPackets.keyAt(i),
-                            elapsedRealtimeMs, uptimeMs);
+                    final int uid = rxPackets.keyAt(i);
                     final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
                             / totalRxPackets;
+                    rxTimesMs.incrementValue(uid, myRxTimeMs);
+                }
+
+                for (int i = 0; i < txTimesMs.size(); i++) {
+                    final int uid = txTimesMs.keyAt(i);
+                    final long myTxTimeMs = txTimesMs.valueAt(i);
                     if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "  RxTime for UID " + uid.getUid() + ": " + myRxTimeMs + " ms");
+                        Slog.d(TAG, "  TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
                     }
-                    uid.getOrCreateWifiControllerActivityLocked().getOrCreateRxTimeCounter()
+                    getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                            .getOrCreateWifiControllerActivityLocked()
+                            .getOrCreateTxTimeCounters()[0]
+                            .increment(myTxTimeMs, elapsedRealtimeMs);
+                    if (uidEstimatedConsumptionMah != null) {
+                        uidEstimatedConsumptionMah.incrementValue(uid,
+                                mWifiPowerCalculator.calcPowerFromControllerDataMah(
+                                        0, myTxTimeMs, 0));
+                    }
+                }
+
+                for (int i = 0; i < rxTimesMs.size(); i++) {
+                    final int uid = rxTimesMs.keyAt(i);
+                    final long myRxTimeMs = rxTimesMs.valueAt(i);
+                    if (DEBUG_ENERGY) {
+                        Slog.d(TAG, "  RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
+                    }
+
+                    getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
+                            .getOrCreateWifiControllerActivityLocked()
+                            .getOrCreateRxTimeCounter()
                             .increment(myRxTimeMs, elapsedRealtimeMs);
                     if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.incrementValue(uid.getUid(),
+                        uidEstimatedConsumptionMah.incrementValue(uid,
                                 mWifiPowerCalculator.calcPowerFromControllerDataMah(
                                         myRxTimeMs, 0, 0));
                     }
@@ -13015,7 +13029,6 @@
 
                 // Any left over power use will be picked up by the WiFi category in BatteryStatsHelper.
 
-
                 // Update WiFi controller stats.
                 mWifiActivity.getOrCreateRxTimeCounter().increment(
                         info.getControllerRxDurationMillis(), elapsedRealtimeMs);
@@ -13353,8 +13366,8 @@
             energy = info.getControllerEnergyUsed();
             if (!info.getUidTraffic().isEmpty()) {
                 for (UidTraffic traffic : info.getUidTraffic()) {
-                    add(uidRxBytes, traffic.getUid(), traffic.getRxBytes());
-                    add(uidTxBytes, traffic.getUid(), traffic.getTxBytes());
+                    uidRxBytes.incrementValue(traffic.getUid(), traffic.getRxBytes());
+                    uidTxBytes.incrementValue(traffic.getUid(), traffic.getTxBytes());
                 }
             }
         }
@@ -13446,6 +13459,9 @@
         long leftOverRxTimeMs = rxTimeMs;
         long leftOverTxTimeMs = txTimeMs;
 
+        final SparseLongArray rxTimesMs = new SparseLongArray(uidCount);
+        final SparseLongArray txTimesMs = new SparseLongArray(uidCount);
+
         for (int i = 0; i < uidCount; i++) {
             final Uid u = mUidStats.valueAt(i);
             if (u.mBluetoothScanTimer == null) {
@@ -13475,12 +13491,8 @@
                     scanTimeTxSinceMarkMs = (txTimeMs * scanTimeTxSinceMarkMs) / totalScanTimeMs;
                 }
 
-                final ControllerActivityCounterImpl counter =
-                        u.getOrCreateBluetoothControllerActivityLocked();
-                counter.getOrCreateRxTimeCounter()
-                        .increment(scanTimeRxSinceMarkMs, elapsedRealtimeMs);
-                counter.getOrCreateTxTimeCounters()[0]
-                        .increment(scanTimeTxSinceMarkMs, elapsedRealtimeMs);
+                rxTimesMs.incrementValue(u.getUid(), scanTimeRxSinceMarkMs);
+                txTimesMs.incrementValue(u.getUid(), scanTimeTxSinceMarkMs);
 
                 if (uidEstimatedConsumptionMah != null) {
                     uidEstimatedConsumptionMah.incrementValue(u.getUid(),
@@ -13544,29 +13556,45 @@
 
                 if (totalRxBytes > 0 && rxBytes > 0) {
                     final long timeRxMs = (leftOverRxTimeMs * rxBytes) / totalRxBytes;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "UID=" + uid + " rx_bytes=" + rxBytes + " rx_time=" + timeRxMs);
-                    }
-                    counter.getOrCreateRxTimeCounter().increment(timeRxMs, elapsedRealtimeMs);
-
-                    if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.incrementValue(u.getUid(),
-                                mBluetoothPowerCalculator.calculatePowerMah(timeRxMs, 0, 0));
-                    }
+                    rxTimesMs.incrementValue(uid, timeRxMs);
                 }
 
                 if (totalTxBytes > 0 && txBytes > 0) {
                     final long timeTxMs = (leftOverTxTimeMs * txBytes) / totalTxBytes;
-                    if (DEBUG_ENERGY) {
-                        Slog.d(TAG, "UID=" + uid + " tx_bytes=" + txBytes + " tx_time=" + timeTxMs);
-                    }
-                    counter.getOrCreateTxTimeCounters()[0]
-                            .increment(timeTxMs, elapsedRealtimeMs);
+                    txTimesMs.incrementValue(uid, timeTxMs);
+                }
+            }
 
-                    if (uidEstimatedConsumptionMah != null) {
-                        uidEstimatedConsumptionMah.incrementValue(u.getUid(),
-                                mBluetoothPowerCalculator.calculatePowerMah(0, timeTxMs, 0));
-                    }
+            for (int i = 0; i < txTimesMs.size(); i++) {
+                final int uid = txTimesMs.keyAt(i);
+                final long myTxTimeMs = txTimesMs.valueAt(i);
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "  TxTime for UID " + uid + ": " + myTxTimeMs + " ms");
+                }
+                getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs)
+                        .getOrCreateBluetoothControllerActivityLocked()
+                        .getOrCreateTxTimeCounters()[0]
+                        .increment(myTxTimeMs, elapsedRealtimeMs);
+                if (uidEstimatedConsumptionMah != null) {
+                    uidEstimatedConsumptionMah.incrementValue(uid,
+                            mBluetoothPowerCalculator.calculatePowerMah(0, myTxTimeMs, 0));
+                }
+            }
+
+            for (int i = 0; i < rxTimesMs.size(); i++) {
+                final int uid = rxTimesMs.keyAt(i);
+                final long myRxTimeMs = rxTimesMs.valueAt(i);
+                if (DEBUG_ENERGY) {
+                    Slog.d(TAG, "  RxTime for UID " + uid + ": " + myRxTimeMs + " ms");
+                }
+
+                getUidStatsLocked(rxTimesMs.keyAt(i), elapsedRealtimeMs, uptimeMs)
+                        .getOrCreateBluetoothControllerActivityLocked()
+                        .getOrCreateRxTimeCounter()
+                        .increment(myRxTimeMs, elapsedRealtimeMs);
+                if (uidEstimatedConsumptionMah != null) {
+                    uidEstimatedConsumptionMah.incrementValue(uid,
+                            mBluetoothPowerCalculator.calculatePowerMah(myRxTimeMs, 0, 0));
                 }
             }
         }
@@ -18156,8 +18184,4 @@
         pw.println();
         dumpMeasuredEnergyStatsLocked(pw);
     }
-
-    private static void add(SparseLongArray array, int key, long delta) {
-        array.put(key, array.get(key) + delta);
-    }
 }
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 5ac4936..def598c 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -85,6 +85,8 @@
     WM_DEBUG_LAYER_MIRRORING(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
             Consts.TAG_WM),
     WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+    WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            "CoreBackPreview"),
     TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
 
     private final boolean mEnabled;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c367e04..9ed1137 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6105,11 +6105,21 @@
     <permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"
         android:protectionLevel="signature" />
 
-      <!-- @SystemApi Allows an application to query over global data in AppSearch.
+    <!-- @SystemApi Allows an application to query over global data in AppSearch.
            @hide -->
     <permission android:name="android.permission.READ_GLOBAL_APP_SEARCH_DATA"
                 android:protectionLevel="internal|role" />
 
+    <!-- Allows an application to query over global data in AppSearch that's visible to the
+         ASSISTANT role.  -->
+    <permission android:name="android.permission.READ_ASSISTANT_APP_SEARCH_DATA"
+        android:protectionLevel="internal|role" />
+
+    <!-- Allows an application to query over global data in AppSearch that's visible to the
+         HOME role.  -->
+    <permission android:name="android.permission.READ_HOME_APP_SEARCH_DATA"
+        android:protectionLevel="internal|role" />
+
     <!-- @SystemApi Allows an application to create virtual devices in VirtualDeviceManager.
          @hide -->
     <permission android:name="android.permission.CREATE_VIRTUAL_DEVICE"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e232d85..6a7b4af 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9376,11 +9376,12 @@
         <attr name="canPauseRecording" format="boolean" />
     </declare-styleable>
 
-    <!-- Use <code>tv-iapp</code> as the root tag of the XML resource that describes a
-         {@link android.media.tv.interactive.TvIAppService}, which is referenced from its
-         {@link android.media.tv.interactive.TvIAppService#SERVICE_META_DATA} meta-data entry.
-         Described here are the attributes that can be included in that tag. -->
-    <declare-styleable name="TvIAppService">
+    <!-- Use <code>tv-interactive-app</code> as the root tag of the XML resource that describes a
+         {@link android.media.tv.interactive.TvInteractiveAppService}, which is referenced
+         from its
+         {@link android.media.tv.interactive.TvInteractiveAppService#SERVICE_META_DATA}
+         meta-data entry. Described here are the attributes that can be included in that tag. -->
+    <declare-styleable name="TvInteractiveAppService">
         <!-- The interactive app types that the TV interactive app service supports.
              Reference to a string array resource that describes the supported types,
              e.g. HbbTv, Ginga. -->
diff --git a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
index d0a13fc..ed035e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BluetoothPowerCalculatorTest.java
@@ -26,6 +26,7 @@
 import android.os.BatteryUsageStatsQuery;
 import android.os.Process;
 import android.os.UidBatteryConsumer;
+import android.os.WorkSource;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -52,6 +53,12 @@
 
     @Test
     public void testTimerBasedModel() {
+        BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
+
+        final WorkSource ws = new WorkSource(APP_UID);
+        batteryStats.noteBluetoothScanStartedFromSourceLocked(ws, false, 0, 0);
+        batteryStats.noteBluetoothScanStoppedFromSourceLocked(ws, false, 1000, 1000);
+
         setupBluetoothEnergyInfo(0, BatteryStats.POWER_DATA_UNAVAILABLE);
 
         BluetoothPowerCalculator calculator =
@@ -59,8 +66,18 @@
 
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
 
-        assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
-                BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.06944, 3000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.19444, 9000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
 
     @Test
@@ -136,8 +153,18 @@
         mStatsRule.apply(new BatteryUsageStatsQuery.Builder().includePowerModels().build(),
                 calculator);
 
-        assertCalculatedPower(0.08216, 0.18169, 0.30030, 0.26386,
-                BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.30030, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
 
     @Test
@@ -150,8 +177,18 @@
 
         mStatsRule.apply(calculator);
 
-        assertCalculatedPower(0.10378, 0.22950, 0.33333, 0.33329,
-                BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.10378, 3583, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.22950, 8416, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.33333, 12000, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.33329, 11999, BatteryConsumer.POWER_MODEL_MEASURED_ENERGY);
     }
 
     @Test
@@ -228,8 +265,18 @@
 
         mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator);
 
-        assertCalculatedPower(0.08216, 0.18169, 0.26388, 0.26386,
-                BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
+                0.08216, 3583, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getUidBatteryConsumer(APP_UID),
+                0.18169, 8416, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getDeviceBatteryConsumer(),
+                0.26388, 12000, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
+        assertBluetoothPowerAndDuration(
+                mStatsRule.getAppsBatteryConsumer(),
+                0.26386, 11999, BatteryConsumer.POWER_MODEL_POWER_PROFILE);
     }
 
     private void setupBluetoothEnergyInfo(long reportedEnergyUc, long consumedEnergyUc) {
@@ -243,22 +290,6 @@
                 consumedEnergyUc, 1000, 1000);
     }
 
-    private void assertCalculatedPower(double bluetoothUidPowerMah, double appPowerMah,
-            double devicePowerMah, double allAppsPowerMah, int powerModelPowerProfile) {
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getUidBatteryConsumer(Process.BLUETOOTH_UID),
-                bluetoothUidPowerMah, 3583, powerModelPowerProfile);
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getUidBatteryConsumer(APP_UID),
-                appPowerMah, 8416, powerModelPowerProfile);
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getDeviceBatteryConsumer(),
-                devicePowerMah, 12000, powerModelPowerProfile);
-        assertBluetoothPowerAndDuration(
-                mStatsRule.getAppsBatteryConsumer(),
-                allAppsPowerMah, 11999, powerModelPowerProfile);
-    }
-
     private void assertBluetoothPowerAndDuration(@Nullable BatteryConsumer batteryConsumer,
             double powerMah, int durationMs, @BatteryConsumer.PowerModel int powerModel) {
         assertThat(batteryConsumer).isNotNull();
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index a787357..a368399 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -92,7 +92,10 @@
         final BatteryStatsImpl batteryStats = setupTestNetworkNumbers();
         final WifiActivityEnergyInfo energyInfo = setupPowerControllerBasedModelEnergyNumbersInfo();
 
-        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 1000, 1000,
+        batteryStats.noteWifiScanStartedLocked(APP_UID, 500, 500);
+        batteryStats.noteWifiScanStoppedLocked(APP_UID, 1500, 1500);
+
+        batteryStats.updateWifiState(energyInfo, POWER_DATA_UNAVAILABLE, 2000, 2000,
                 mNetworkStatsManager);
 
         WifiPowerCalculator calculator = new WifiPowerCalculator(mStatsRule.getPowerProfile());
@@ -100,15 +103,15 @@
 
         UidBatteryConsumer uidConsumer = mStatsRule.getUidBatteryConsumer(APP_UID);
         assertThat(uidConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(1423);
+                .isEqualTo(2473);
         assertThat(uidConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isWithin(PRECISION).of(0.2214666);
+                .isWithin(PRECISION).of(0.3964);
         assertThat(uidConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE);
 
         BatteryConsumer deviceConsumer = mStatsRule.getDeviceBatteryConsumer();
         assertThat(deviceConsumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_WIFI))
-                .isEqualTo(4002);
+                .isEqualTo(4001);
         assertThat(deviceConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WIFI))
                 .isWithin(PRECISION).of(0.86666);
         assertThat(deviceConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_WIFI))
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 6f5951b..1068c27 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -394,6 +394,9 @@
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+        <!-- Permission required for CTS test - TrustTestCases -->
+        <permission name="android.permission.PROVIDE_TRUST_AGENT" />
+        <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
         <!-- Permissions required for Incremental CTS tests -->
         <permission name="com.android.permission.USE_INSTALLER_V2"/>
         <permission name="android.permission.LOADER_USAGE_STATS"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 535d656..9b67cfc 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -103,18 +103,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/TaskFragment.java"
     },
-    "-2002500255": {
-      "message": "Defer removing snapshot surface in %dms",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
-    },
-    "-1991255017": {
-      "message": "Drawing snapshot surface sizeMismatch=%b",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
-    },
     "-1980468143": {
       "message": "DisplayArea appeared name=%s",
       "level": "VERBOSE",
@@ -745,6 +733,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1343787701": {
+      "message": "startBackNavigation task=%s, topRunningActivity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "-1340540100": {
       "message": "Creating SnapshotStartingData",
       "level": "VERBOSE",
@@ -1597,12 +1591,6 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "-405536909": {
-      "message": "Removing snapshot surface",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
-    },
     "-401282500": {
       "message": "destroyIfPossible: r=%s destroy returned removed=%s",
       "level": "DEBUG",
@@ -1867,6 +1855,12 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/Task.java"
     },
+    "-134091882": {
+      "message": "Screenshotting Activity %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/TaskFragment.java"
+    },
     "-124316973": {
       "message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
       "level": "VERBOSE",
@@ -1951,6 +1945,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/WindowContainer.java"
     },
+    "-23020844": {
+      "message": "Back: Reset surfaces",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "-21399771": {
       "message": "activity %s already destroying, skipping request with reason:%s",
       "level": "VERBOSE",
@@ -2005,12 +2005,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "44438983": {
-      "message": "performLayout: Activity exiting now removed %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ADD_REMOVE",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "45285419": {
       "message": "startingWindow was set but startingSurface==null, couldn't remove",
       "level": "VERBOSE",
@@ -3271,12 +3265,6 @@
       "group": "WM_DEBUG_LAYER_MIRRORING",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
-    "1417601133": {
-      "message": "Enqueueing ADD_STARTING",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_STARTING_WINDOW",
-      "at": "com\/android\/server\/wm\/ActivityRecord.java"
-    },
     "1422781269": {
       "message": "Resuming rotation after re-position",
       "level": "DEBUG",
@@ -3397,6 +3385,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1554795024": {
+      "message": "Previous Activity is %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_BACK_PREVIEW",
+      "at": "com\/android\/server\/wm\/BackNavigationController.java"
+    },
     "1557732761": {
       "message": "For Intent %s bringing to top: %s",
       "level": "DEBUG",
@@ -3924,6 +3918,9 @@
     "WM_DEBUG_APP_TRANSITIONS_ANIM": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_BACK_PREVIEW": {
+      "tag": "CoreBackPreview"
+    },
     "WM_DEBUG_BOOT": {
       "tag": "WindowManager"
     },
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
new file mode 100644
index 0000000..b310dd6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -0,0 +1,35 @@
+/*
+ * 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.wm.shell.back;
+
+import android.view.MotionEvent;
+
+/**
+ * Interface for SysUI to get access to the Back animation related methods.
+ */
+public interface BackAnimation {
+
+    /**
+     * Called when a {@link MotionEvent} is generated by a back gesture.
+     */
+    void onBackMotion(MotionEvent event);
+
+    /**
+     * Sets whether the back gesture is past the trigger threshold or not.
+     */
+    void setTriggerBack(boolean triggerBack);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
new file mode 100644
index 0000000..229e8ee0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -0,0 +1,286 @@
+/*
+ * 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.wm.shell.back;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+
+/**
+ * Controls the window animation run when a user initiates a back gesture.
+ */
+public class BackAnimationController {
+
+    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+    public static final boolean IS_ENABLED = SystemProperties
+            .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+    private static final String TAG = "BackAnimationController";
+
+    /**
+     * Location of the initial touch event of the back gesture.
+     */
+    private final PointF mInitTouchLocation = new PointF();
+
+    /**
+     * Raw delta between {@link #mInitTouchLocation} and the last touch location.
+     */
+    private final Point mTouchEventDelta = new Point();
+    private final ShellExecutor mShellExecutor;
+
+    /** True when a back gesture is ongoing */
+    private boolean mBackGestureStarted = false;
+
+    /** @see #setTriggerBack(boolean) */
+    private boolean mTriggerBack;
+
+    @Nullable
+    private BackNavigationInfo mBackNavigationInfo;
+    private final SurfaceControl.Transaction mTransaction;
+    private final IActivityTaskManager mActivityTaskManager;
+
+    public BackAnimationController(@ShellMainThread ShellExecutor shellExecutor) {
+        this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService());
+    }
+
+    @VisibleForTesting
+    BackAnimationController(@NonNull ShellExecutor shellExecutor,
+            @NonNull SurfaceControl.Transaction transaction,
+            @NonNull IActivityTaskManager activityTaskManager) {
+        mShellExecutor = shellExecutor;
+        mTransaction = transaction;
+        mActivityTaskManager = activityTaskManager;
+    }
+
+    public BackAnimation getBackAnimationImpl() {
+        return mBackAnimation;
+    }
+
+    private final BackAnimation mBackAnimation = new BackAnimationImpl();
+
+    private class BackAnimationImpl implements BackAnimation {
+
+        @Override
+        public void onBackMotion(MotionEvent event) {
+            mShellExecutor.execute(() -> onMotionEvent(event));
+        }
+
+        @Override
+        public void setTriggerBack(boolean triggerBack) {
+            mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
+        }
+    }
+
+    /**
+     * Called when a new motion event needs to be transferred to this
+     * {@link BackAnimationController}
+     */
+    public void onMotionEvent(MotionEvent event) {
+        int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            initAnimation(event);
+        } else if (action == MotionEvent.ACTION_MOVE) {
+            onMove(event);
+        } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            onGestureFinished();
+        }
+    }
+
+    private void initAnimation(MotionEvent event) {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "initAnimation mMotionStarted=%b", mBackGestureStarted);
+        if (mBackGestureStarted) {
+            Log.e(TAG, "Animation is being initialized but is already started.");
+            return;
+        }
+
+        if (mBackNavigationInfo != null) {
+            finishAnimation();
+        }
+        mInitTouchLocation.set(event.getX(), event.getY());
+        mBackGestureStarted = true;
+
+        try {
+            mBackNavigationInfo = mActivityTaskManager.startBackNavigation();
+            onBackNavigationInfoReceived(mBackNavigationInfo);
+        } catch (RemoteException remoteException) {
+            Log.e(TAG, "Failed to initAnimation", remoteException);
+            finishAnimation();
+        }
+    }
+
+    private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo) {
+        if (backNavigationInfo == null
+                || backNavigationInfo.getDepartingWindowContainer() == null) {
+            Log.e(TAG, "Received BackNavigationInfo is null.");
+            finishAnimation();
+            return;
+        }
+
+        HardwareBuffer hardwareBuffer = backNavigationInfo.getScreenshotHardwareBuffer();
+        if (hardwareBuffer != null) {
+            displayTargetScreenshot(hardwareBuffer,
+                    backNavigationInfo.getTaskWindowConfiguration());
+        }
+        mTransaction.apply();
+    }
+
+    /**
+     * Display the screenshot of the activity beneath.
+     *
+     * @param hardwareBuffer The buffer containing the screenshot.
+     */
+    private void displayTargetScreenshot(@NonNull HardwareBuffer hardwareBuffer,
+            WindowConfiguration taskWindowConfiguration) {
+        SurfaceControl screenshotSurface =
+                mBackNavigationInfo == null ? null : mBackNavigationInfo.getScreenshotSurface();
+        if (screenshotSurface == null) {
+            Log.e(TAG, "BackNavigationInfo doesn't contain a surface for the screenshot. ");
+            return;
+        }
+
+        // Scale the buffer to fill the whole Task
+        float sx = 1;
+        float sy = 1;
+        float w = taskWindowConfiguration.getBounds().width();
+        float h = taskWindowConfiguration.getBounds().height();
+
+        if (w != hardwareBuffer.getWidth()) {
+            sx = w / hardwareBuffer.getWidth();
+        }
+
+        if (h != hardwareBuffer.getHeight()) {
+            sy = h / hardwareBuffer.getHeight();
+        }
+        mTransaction.setScale(screenshotSurface, sx, sy);
+        mTransaction.setBuffer(screenshotSurface, hardwareBuffer);
+        mTransaction.setVisibility(screenshotSurface, true);
+    }
+
+    private void onMove(MotionEvent event) {
+        if (!mBackGestureStarted || mBackNavigationInfo == null) {
+            return;
+        }
+        int deltaX = Math.round(event.getX() - mInitTouchLocation.x);
+        int deltaY = Math.round(event.getY() - mInitTouchLocation.y);
+        ProtoLog.v(WM_SHELL_BACK_PREVIEW, "Runner move: %d %d", deltaX, deltaY);
+        SurfaceControl topWindowLeash = mBackNavigationInfo.getDepartingWindowContainer();
+        mTransaction.setPosition(topWindowLeash, deltaX, deltaY);
+        mTouchEventDelta.set(deltaX, deltaY);
+        mTransaction.apply();
+    }
+
+    private void onGestureFinished() {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", mTriggerBack);
+        if (mBackGestureStarted) {
+            if (mTriggerBack) {
+                prepareTransition();
+            } else {
+                resetPositionAnimated();
+            }
+        }
+        mBackGestureStarted = false;
+        mTriggerBack = false;
+    }
+
+    /**
+     * Animate the top window leash to its initial position.
+     */
+    private void resetPositionAnimated() {
+        mBackGestureStarted = false;
+        // TODO(208786853) Handle overlap with a new coming gesture.
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Runner: Back not triggered, cancelling animation "
+                + "mLastPos=%s mInitTouch=%s", mTouchEventDelta, mInitTouchLocation);
+
+        // TODO(208427216) : Replace placeholder animation with an actual one.
+        ValueAnimator animation = ValueAnimator.ofFloat(0f, 1f).setDuration(200);
+        animation.addUpdateListener(animation1 -> {
+            if (mBackNavigationInfo == null) {
+                return;
+            }
+            float fraction = animation1.getAnimatedFraction();
+            int deltaX = Math.round(mTouchEventDelta.x - (mTouchEventDelta.x * fraction));
+            int deltaY = Math.round(mTouchEventDelta.y - (mTouchEventDelta.y * fraction));
+            mTransaction.setPosition(mBackNavigationInfo.getDepartingWindowContainer(),
+                    deltaX, deltaY);
+            mTransaction.apply();
+        });
+
+        animation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onAnimationEnd");
+                finishAnimation();
+            }
+        });
+        animation.start();
+    }
+
+    private void prepareTransition() {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "prepareTransition()");
+        mTriggerBack = false;
+        mBackGestureStarted = false;
+    }
+
+    /**
+     * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
+     */
+    public void setTriggerBack(boolean triggerBack) {
+        mTriggerBack = triggerBack;
+    }
+
+    private void finishAnimation() {
+        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishAnimation()");
+        mBackGestureStarted = false;
+        mTouchEventDelta.set(0, 0);
+        mInitTouchLocation.set(0, 0);
+        BackNavigationInfo backNavigationInfo = mBackNavigationInfo;
+        mBackNavigationInfo = null;
+        if (backNavigationInfo == null) {
+            return;
+        }
+        SurfaceControl topWindowLeash = backNavigationInfo.getDepartingWindowContainer();
+        if (topWindowLeash != null && topWindowLeash.isValid()) {
+            mTransaction.remove(topWindowLeash);
+        }
+        SurfaceControl screenshotSurface = backNavigationInfo.getScreenshotSurface();
+        if (screenshotSurface != null && screenshotSurface.isValid()) {
+            mTransaction.remove(screenshotSurface);
+        }
+        mTransaction.apply();
+        backNavigationInfo.onBackNavigationFinished();
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
deleted file mode 100644
index dc20f7b..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackPreviewHandler.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.wm.shell.back;
-
-import android.os.SystemProperties;
-import android.view.IWindowManager;
-
-import javax.inject.Inject;
-
-/**
- * Handle the preview of what a back gesture will lead to.
- */
-public class BackPreviewHandler {
-
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-
-    public static boolean isEnabled() {
-        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
-    }
-
-    private final IWindowManager mWmService;
-
-    @Inject
-    public BackPreviewHandler(IWindowManager windowManagerService) {
-        mWmService = windowManagerService;
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 23d9b8b..f61e624 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -40,6 +40,8 @@
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.apppairs.AppPairs;
 import com.android.wm.shell.apppairs.AppPairsController;
+import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.back.BackAnimationController;
 import com.android.wm.shell.bubbles.BubbleController;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.common.DisplayController;
@@ -238,6 +240,17 @@
     }
 
     //
+    // Back animation
+    //
+
+    @WMSingleton
+    @Provides
+    static Optional<BackAnimation> provideBackAnimation(
+            Optional<BackAnimationController> backAnimationController) {
+        return backAnimationController.map(BackAnimationController::getBackAnimationImpl);
+    }
+
+    //
     // Bubbles (optional feature)
     //
 
@@ -678,4 +691,16 @@
                 legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
                 hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
     }
+
+    @WMSingleton
+    @Provides
+    static Optional<BackAnimationController> provideBackAnimationController(
+            @ShellMainThread ShellExecutor shellExecutor
+    ) {
+        if (BackAnimationController.IS_ENABLED) {
+            return Optional.of(
+                    new BackAnimationController(shellExecutor));
+        }
+        return Optional.empty();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 79c1df2..20c4e21 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -34,6 +34,8 @@
             Consts.TAG_WM_SHELL),
     WM_SHELL_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM_STARTING_WINDOW),
+    WM_SHELL_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+            "ShellBackPreview"),
     TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
 
     private final boolean mEnabled;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
new file mode 100644
index 0000000..8446b37
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
new file mode 100644
index 0000000..566acc8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Bubbles
+# Bug component: 555586
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
new file mode 100644
index 0000000..8446b37
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Split Screen
+# Bug component: 928697
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
new file mode 100644
index 0000000..172e24bf
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/OWNERS
@@ -0,0 +1,2 @@
+# window manager > wm shell > Picture-In-Picture
+# Bug component: 316251
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
new file mode 100644
index 0000000..960c7ac
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.back;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.IActivityTaskManager;
+import android.app.WindowConfiguration;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ShellExecutor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * atest WMShellUnitTests:BackAnimationControllerTest
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BackAnimationControllerTest {
+
+    private final ShellExecutor mShellExecutor = new TestShellExecutor();
+
+    @Mock
+    private SurfaceControl.Transaction mTransaction;
+
+    @Mock
+    private IActivityTaskManager mActivityTaskManager;
+
+    private BackAnimationController mController;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mController = new BackAnimationController(
+                mShellExecutor, mTransaction, mActivityTaskManager);
+    }
+
+    private void createNavigationInfo(SurfaceControl topWindowLeash,
+            SurfaceControl screenshotSurface,
+            HardwareBuffer hardwareBuffer) {
+        BackNavigationInfo navigationInfo = new BackNavigationInfo(
+                BackNavigationInfo.TYPE_RETURN_TO_HOME,
+                topWindowLeash,
+                screenshotSurface,
+                hardwareBuffer,
+                new WindowConfiguration(),
+                new RemoteCallback((bundle) -> {}));
+        try {
+            doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation();
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    @Test
+    public void screenshotAttachedAndVisible() {
+        SurfaceControl topWindowLeash = new SurfaceControl();
+        SurfaceControl screenshotSurface = new SurfaceControl();
+        HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+        createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+        mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+        verify(mTransaction).setBuffer(screenshotSurface, hardwareBuffer);
+        verify(mTransaction).setVisibility(screenshotSurface, true);
+        verify(mTransaction).apply();
+    }
+
+    @Test
+    public void surfaceMovesWithGesture() {
+        SurfaceControl topWindowLeash = new SurfaceControl();
+        SurfaceControl screenshotSurface = new SurfaceControl();
+        HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
+        createNavigationInfo(topWindowLeash, screenshotSurface, hardwareBuffer);
+        mController.onMotionEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0));
+        mController.onMotionEvent(MotionEvent.obtain(10, 0, MotionEvent.ACTION_MOVE, 100, 100, 0));
+        verify(mTransaction).setPosition(topWindowLeash, 100, 100);
+        verify(mTransaction, atLeastOnce()).apply();
+    }
+}
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 1fc2cf9..6168c22 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -18,12 +18,16 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
 import android.graphics.GraphicBuffer;
 import android.graphics.ImageFormat;
 import android.graphics.ImageFormat.Format;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
 import android.hardware.HardwareBuffer;
+import android.hardware.HardwareBuffer.Usage;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.hardware.camera2.utils.SurfaceUtils;
 import android.os.Handler;
@@ -95,10 +99,18 @@
     private ListenerHandler mListenerHandler;
     private long mNativeContext;
 
+    private int mWidth;
+    private int mHeight;
+    private final int mMaxImages;
+    private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
+    private @HardwareBuffer.Format int mHardwareBufferFormat;
+    private @NamedDataSpace long mDataSpace;
+    private boolean mUseLegacyImageFormat;
+    private boolean mUseSurfaceImageFormatInfo;
+
     // Field below is used by native code, do not access or modify.
     private int mWriterFormat;
 
-    private final int mMaxImages;
     // Keep track of the currently dequeued Image. This need to be thread safe as the images
     // could be closed by different threads (e.g., application thread and GC thread).
     private List<Image> mDequeuedImages = new CopyOnWriteArrayList<>();
@@ -131,7 +143,7 @@
      */
     public static @NonNull ImageWriter newInstance(@NonNull Surface surface,
             @IntRange(from = 1) int maxImages) {
-        return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN, -1 /*width*/,
+        return new ImageWriter(surface, maxImages, true, ImageFormat.UNKNOWN, -1 /*width*/,
                 -1 /*height*/);
     }
 
@@ -183,7 +195,7 @@
         if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
             throw new IllegalArgumentException("Invalid format is specified: " + format);
         }
-        return new ImageWriter(surface, maxImages, format, width, height);
+        return new ImageWriter(surface, maxImages, false, format, width, height);
     }
 
     /**
@@ -232,48 +244,49 @@
         if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) {
             throw new IllegalArgumentException("Invalid format is specified: " + format);
         }
-        return new ImageWriter(surface, maxImages, format, -1 /*width*/, -1 /*height*/);
+        return new ImageWriter(surface, maxImages, false, format, -1 /*width*/, -1 /*height*/);
     }
 
-    /**
-     * @hide
-     */
-    protected ImageWriter(Surface surface, int maxImages, int format, int width, int height) {
+    private void initializeImageWriter(Surface surface, int maxImages,
+            boolean useSurfaceImageFormatInfo, boolean useLegacyImageFormat, int imageFormat,
+            int hardwareBufferFormat, long dataSpace, int width, int height, long usage) {
         if (surface == null || maxImages < 1) {
             throw new IllegalArgumentException("Illegal input argument: surface " + surface
-                    + ", maxImages: " + maxImages);
+                + ", maxImages: " + maxImages);
         }
 
-        mMaxImages = maxImages;
-
+        mUseSurfaceImageFormatInfo = useSurfaceImageFormatInfo;
+        mUseLegacyImageFormat = useLegacyImageFormat;
         // Note that the underlying BufferQueue is working in synchronous mode
         // to avoid dropping any buffers.
-        mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, format, width,
-                height);
+        mNativeContext = nativeInit(new WeakReference<>(this), surface, maxImages, width, height,
+            useSurfaceImageFormatInfo, hardwareBufferFormat, dataSpace, usage);
 
-        // nativeInit internally overrides UNKNOWN format. So does surface format query after
-        // nativeInit and before getEstimatedNativeAllocBytes().
-        if (format == ImageFormat.UNKNOWN) {
-            format = SurfaceUtils.getSurfaceFormat(surface);
-        }
-        // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
-        // allocation estimation sequence depends on the public formats values. To avoid
-        // possible errors, convert where necessary.
-        if (format == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
-            int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
-            switch (surfaceDataspace) {
-                case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
-                    format = ImageFormat.DEPTH_POINT_CLOUD;
-                    break;
-                case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
-                    format = ImageFormat.DEPTH_JPEG;
-                    break;
-                case StreamConfigurationMap.HAL_DATASPACE_HEIF:
-                    format = ImageFormat.HEIC;
-                    break;
-                default:
-                    format = ImageFormat.JPEG;
+        if (useSurfaceImageFormatInfo) {
+            // nativeInit internally overrides UNKNOWN format. So does surface format query after
+            // nativeInit and before getEstimatedNativeAllocBytes().
+            imageFormat = SurfaceUtils.getSurfaceFormat(surface);
+            // Several public formats use the same native HAL_PIXEL_FORMAT_BLOB. The native
+            // allocation estimation sequence depends on the public formats values. To avoid
+            // possible errors, convert where necessary.
+            if (imageFormat == StreamConfigurationMap.HAL_PIXEL_FORMAT_BLOB) {
+                int surfaceDataspace = SurfaceUtils.getSurfaceDataspace(surface);
+                switch (surfaceDataspace) {
+                    case StreamConfigurationMap.HAL_DATASPACE_DEPTH:
+                        imageFormat = ImageFormat.DEPTH_POINT_CLOUD;
+                        break;
+                    case StreamConfigurationMap.HAL_DATASPACE_DYNAMIC_DEPTH:
+                        imageFormat = ImageFormat.DEPTH_JPEG;
+                        break;
+                    case StreamConfigurationMap.HAL_DATASPACE_HEIF:
+                        imageFormat = ImageFormat.HEIC;
+                        break;
+                    default:
+                        imageFormat = ImageFormat.JPEG;
+                }
             }
+            mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+            mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
         }
         // Estimate the native buffer allocation size and register it so it gets accounted for
         // during GC. Note that this doesn't include the buffers required by the buffer queue
@@ -282,12 +295,49 @@
         // complex, and 1 buffer is enough for the VM to treat the ImageWriter as being of some
         // size.
         Size surfSize = SurfaceUtils.getSurfaceSize(surface);
+        mWidth = width == -1 ? surfSize.getWidth() : width;
+        mHeight = height == -1 ? surfSize.getHeight() : height;
+
         mEstimatedNativeAllocBytes =
-                ImageUtils.getEstimatedNativeAllocBytes(surfSize.getWidth(),surfSize.getHeight(),
-                        format, /*buffer count*/ 1);
+            ImageUtils.getEstimatedNativeAllocBytes(mWidth, mHeight,
+                useLegacyImageFormat ? imageFormat : hardwareBufferFormat, /*buffer count*/ 1);
         VMRuntime.getRuntime().registerNativeAllocation(mEstimatedNativeAllocBytes);
     }
 
+    private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+            int imageFormat, int width, int height) {
+        mMaxImages = maxImages;
+        // update hal format and dataspace only if image format is overridden by producer.
+        mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+        mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+
+        initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true,
+                imageFormat, mHardwareBufferFormat, mDataSpace, width, height, mUsage);
+    }
+
+    private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+            int imageFormat, int width, int height, long usage) {
+        mMaxImages = maxImages;
+        mUsage = usage;
+        mHardwareBufferFormat = PublicFormatUtils.getHalFormat(imageFormat);
+        mDataSpace = PublicFormatUtils.getHalDataspace(imageFormat);
+
+        initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, true,
+                imageFormat, mHardwareBufferFormat, mDataSpace, width, height, usage);
+    }
+
+    private ImageWriter(Surface surface, int maxImages, boolean useSurfaceImageFormatInfo,
+            int hardwareBufferFormat, long dataSpace, int width, int height, long usage) {
+        mMaxImages = maxImages;
+        mUsage = usage;
+        mHardwareBufferFormat = hardwareBufferFormat;
+        mDataSpace = dataSpace;
+        int publicFormat = PublicFormatUtils.getPublicFormat(hardwareBufferFormat, dataSpace);
+
+        initializeImageWriter(surface, maxImages, useSurfaceImageFormatInfo, false,
+                publicFormat, hardwareBufferFormat, dataSpace, width, height, usage);
+    }
+
     /**
      * <p>
      * Maximum number of Images that can be dequeued from the ImageWriter
@@ -316,6 +366,30 @@
     }
 
     /**
+     * The width of {@link Image Images}, in pixels.
+     *
+     * <p>If {@link Builder#setWidthAndHeight} is not called, the default width of the Image
+     * depends on the Surface provided by customer end-point.</p>
+     *
+     * @return the expected actual width of an Image.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * The height of {@link Image Images}, in pixels.
+     *
+     * <p>If {@link Builder#setWidthAndHeight} is not called, the default height of the Image
+     * depends on the Surface provided by customer end-point.</p>
+     *
+     * @return the expected height of an Image.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    /**
      * <p>
      * Dequeue the next available input Image for the application to produce
      * data into.
@@ -490,6 +564,41 @@
     }
 
     /**
+     * Get the ImageWriter usage flag.
+     *
+     * @return The ImageWriter usage flag.
+     */
+    public @Usage long getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Get the ImageWriter hardwareBuffer format.
+     *
+     * <p>Use this function if the ImageWriter instance is created by builder pattern
+     * {@code ImageWriter.Builder} and using {@link Builder#setHardwareBufferFormat} and
+     * {@link Builder#setDataSpace}.</p>
+     *
+     * @return The ImageWriter hardwareBuffer format.
+     */
+    public @HardwareBuffer.Format int getHardwareBufferFormat() {
+        return mHardwareBufferFormat;
+    }
+
+    /**
+     * Get the ImageWriter dataspace.
+     *
+     * <p>Use this function if the ImageWriter instance is created by builder pattern
+     * {@code ImageWriter.Builder} and {@link Builder#setDataSpace}.</p>
+     *
+     * @return The ImageWriter dataspace.
+     */
+    @SuppressLint("MethodNameUnits")
+    public @NamedDataSpace long getDataSpace() {
+        return mDataSpace;
+    }
+
+    /**
      * ImageWriter callback interface, used to to asynchronously notify the
      * application of various ImageWriter events.
      */
@@ -755,6 +864,155 @@
         return true;
     }
 
+    /**
+     * Builder class for {@link ImageWriter} objects.
+     */
+    public static final class Builder {
+        private Surface mSurface;
+        private int mWidth = -1;
+        private int mHeight = -1;
+        private int mMaxImages = 1;
+        private int mImageFormat = ImageFormat.UNKNOWN;
+        private @Usage long mUsage = HardwareBuffer.USAGE_CPU_WRITE_OFTEN;
+        private @HardwareBuffer.Format int mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+        private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+        private boolean mUseSurfaceImageFormatInfo = true;
+        // set this as true temporarily now as a workaround to get correct format
+        // when using surface format by default without overriding the image format
+        // in the builder pattern
+        private boolean mUseLegacyImageFormat = true;
+
+        /**
+         * Constructs a new builder for {@link ImageWriter}.
+         *
+         * @param surface The destination Surface this writer produces Image data into.
+         */
+        public Builder(@NonNull Surface surface) {
+            mSurface = surface;
+        }
+
+        /**
+         * Set the width and height of images. Default size is dependent on the Surface that is
+         * provided by the downstream end-point.
+         *
+         * @param width The width in pixels that will be passed to the producer.
+         * @param height The height in pixels that will be passed to the producer.
+         * @return the Builder instance with customized width and height.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setWidthAndHeight(@IntRange(from = 1) int width,
+                @IntRange(from = 1) int height) {
+            mWidth = width;
+            mHeight = height;
+            return this;
+        }
+
+        /**
+         * Set the maximum number of images. Default value is 1.
+         *
+         * @param maxImages The maximum number of Images the user will want to access simultaneously
+         *                  for producing Image data.
+         * @return the Builder instance with customized usage value.
+         */
+        public @NonNull Builder setMaxImages(@IntRange(from = 1) int maxImages) {
+            mMaxImages = maxImages;
+            return this;
+        }
+
+        /**
+         * Set the image format of this ImageWriter.
+         * Default format depends on the Surface provided.
+         *
+         * @param imageFormat The format of the {@link ImageWriter}. It can be any valid specified
+         *                    by {@link ImageFormat} or {@link PixelFormat}.
+         * @return the Builder instance with customized image format.
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        public @NonNull Builder setImageFormat(@Format int imageFormat) {
+            if (!ImageFormat.isPublicFormat(imageFormat)
+                    && !PixelFormat.isPublicFormat(imageFormat)) {
+                throw new IllegalArgumentException(
+                        "Invalid imageFormat is specified: " + imageFormat);
+            }
+            mImageFormat = imageFormat;
+            mUseLegacyImageFormat = true;
+            mHardwareBufferFormat = HardwareBuffer.RGBA_8888;
+            mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+            mUseSurfaceImageFormatInfo = false;
+            return this;
+        }
+
+        /**
+         * Set the hardwareBuffer format of this ImageWriter. The default value is
+         * {@link HardwareBuffer#RGBA_8888 HardwareBuffer.RGBA_8888}.
+         *
+         * <p>This function works together with {@link #setDataSpace} for an
+         * {@link ImageWriter} instance. Setting at least one of these two replaces
+         * {@link #setImageFormat} function.</p>
+         *
+         * @param hardwareBufferFormat The HardwareBuffer format of the image that this writer
+         *                             will produce.
+         * @return the Builder instance with customized buffer format.
+         *
+         * @see #setDataSpace
+         * @see #setImageFormat
+         */
+        public @NonNull Builder setHardwareBufferFormat(
+                @HardwareBuffer.Format int hardwareBufferFormat) {
+            mHardwareBufferFormat = hardwareBufferFormat;
+            mImageFormat = ImageFormat.UNKNOWN;
+            mUseLegacyImageFormat = false;
+            mUseSurfaceImageFormatInfo = false;
+            return this;
+        }
+
+        /**
+         * Set the dataspace of this ImageWriter.
+         * The default value is {@link DataSpace#DATASPACE_UNKNOWN}.
+         *
+         * @param dataSpace The dataspace of the image that this writer will produce.
+         * @return the builder instance with customized dataspace value.
+         *
+         * @see #setHardwareBufferFormat
+         */
+        public @NonNull Builder setDataSpace(@NamedDataSpace long dataSpace) {
+            mDataSpace = dataSpace;
+            mImageFormat = ImageFormat.UNKNOWN;
+            mUseLegacyImageFormat = false;
+            mUseSurfaceImageFormatInfo = false;
+            return this;
+        }
+
+        /**
+         * Set the usage flag of this ImageWriter.
+         * Default value is {@link HardwareBuffer#USAGE_CPU_WRITE_OFTEN}.
+         *
+         * @param usage The intended usage of the images produced by this ImageWriter.
+         * @return the Builder instance with customized usage flag.
+         *
+         * @see HardwareBuffer
+         */
+        public @NonNull Builder setUsage(@Usage long usage) {
+            mUsage = usage;
+            return this;
+        }
+
+        /**
+         * Builds a new ImageWriter object.
+         *
+         * @return The new ImageWriter object.
+         */
+        public @NonNull ImageWriter build() {
+            if (mUseLegacyImageFormat) {
+                return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo,
+                        mImageFormat, mWidth, mHeight, mUsage);
+            } else {
+                return new ImageWriter(mSurface, mMaxImages, mUseSurfaceImageFormatInfo,
+                        mHardwareBufferFormat, mDataSpace, mWidth, mHeight, mUsage);
+            }
+        }
+    }
+
     private static class WriterSurfaceImage extends android.media.Image {
         private ImageWriter mOwner;
         // This field is used by native code, do not access or modify.
@@ -774,6 +1032,13 @@
 
         public WriterSurfaceImage(ImageWriter writer) {
             mOwner = writer;
+            mWidth = writer.mWidth;
+            mHeight = writer.mHeight;
+
+            if (!writer.mUseLegacyImageFormat) {
+                mFormat = PublicFormatUtils.getPublicFormat(
+                        writer.mHardwareBufferFormat, writer.mDataSpace);
+            }
         }
 
         @Override
@@ -969,8 +1234,9 @@
     }
 
     // Native implemented ImageWriter methods.
-    private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImgs,
-            int format, int width, int height);
+    private synchronized native long nativeInit(Object weakSelf, Surface surface, int maxImages,
+            int width, int height, boolean useSurfaceImageFormatInfo, int hardwareBufferFormat,
+            long dataSpace, long usage);
 
     private synchronized native void nativeClose(long nativeCtx);
 
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 3c152fb..e75df1d 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -603,6 +603,18 @@
         public static final String FEATURE_QpBounds = "qp-bounds";
 
         /**
+         * <b>video encoder only</b>: codec supports exporting encoding statistics.
+         * Encoders with this feature can provide the App clients with the encoding statistics
+         * information about the frame.
+         * The scope of encoding statistics is controlled by
+         * {@link MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL}.
+         *
+         * @see MediaFormat#KEY_VIDEO_ENCODING_STATISTICS_LEVEL
+         */
+        @SuppressLint("AllUpper") // for consistency with other FEATURE_* constants
+        public static final String FEATURE_EncodingStatistics = "encoding-statistics";
+
+        /**
          * Query codec feature capabilities.
          * <p>
          * These features are supported to be used by the codec.  These
@@ -641,6 +653,7 @@
             new Feature(FEATURE_MultipleFrames, (1 << 1), false),
             new Feature(FEATURE_DynamicTimestamp, (1 << 2), false),
             new Feature(FEATURE_QpBounds, (1 << 3), false),
+            new Feature(FEATURE_EncodingStatistics, (1 << 4), false),
             // feature to exclude codec from REGULAR codec list
             new Feature(FEATURE_SpecialCodec,     (1 << 30), false, true),
         };
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 4891d74..4956dbe 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1176,6 +1176,76 @@
     public static final String KEY_VIDEO_QP_B_MIN = "video-qp-b-min";
 
     /**
+     * A key describing the level of encoding statistics information emitted from video encoder.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_ENCODING_STATISTICS_LEVEL =
+            "video-encoding-statistics-level";
+
+    /**
+     * Encoding Statistics Level None.
+     * Encoder generates no information about Encoding statistics.
+     */
+    public static final int VIDEO_ENCODING_STATISTICS_LEVEL_NONE = 0;
+
+    /**
+     * Encoding Statistics Level 1.
+     * Encoder generates {@link MediaFormat#KEY_PICTURE_TYPE} and
+     * {@link MediaFormat#KEY_VIDEO_QP_AVERAGE} for each frame.
+     */
+    public static final int VIDEO_ENCODING_STATISTICS_LEVEL_1 = 1;
+
+    /** @hide */
+    @IntDef({
+        VIDEO_ENCODING_STATISTICS_LEVEL_NONE,
+        VIDEO_ENCODING_STATISTICS_LEVEL_1,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface VideoEncodingStatisticsLevel {}
+
+    /**
+     * A key describing the per-frame average block QP (Quantization Parameter).
+     * This is a part of a video 'Encoding Statistics' export feature.
+     * This value is emitted from video encoder for a video frame.
+     * The average value is rounded down (using floor()) to integer value.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_VIDEO_QP_AVERAGE = "video-qp-average";
+
+    /**
+     * A key describing the picture type of the encoded frame.
+     * This is a part of a video 'Encoding Statistics' export feature.
+     * This value is emitted from video encoder for a video frame.
+     *
+     * The associated value is an integer.
+     */
+    public static final String KEY_PICTURE_TYPE = "picture-type";
+
+    /** Picture Type is unknown. */
+    public static final int PICTURE_TYPE_UNKNOWN = 0;
+
+    /** Picture Type is I Frame. */
+    public static final int PICTURE_TYPE_I = 1;
+
+    /** Picture Type is P Frame. */
+    public static final int PICTURE_TYPE_P = 2;
+
+    /** Picture Type is B Frame. */
+    public static final int PICTURE_TYPE_B = 3;
+
+    /** @hide */
+    @IntDef({
+        PICTURE_TYPE_UNKNOWN,
+        PICTURE_TYPE_I,
+        PICTURE_TYPE_P,
+        PICTURE_TYPE_B,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PictureType {}
+
+    /**
      * A key describing the audio session ID of the AudioTrack associated
      * to a tunneled video codec.
      * The associated value is an integer.
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index a049a88..831649e 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -31,12 +31,15 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.concurrent.ConcurrentHashMap;
 
+// BLE-MIDI
+
 /**
  * This class is the public application interface to the MIDI service.
  */
@@ -399,9 +402,11 @@
         final OnDeviceOpenedListener listenerF = listener;
         final Handler handlerF = handler;
 
+        Log.d(TAG, "openBluetoothDevice() " + bluetoothDevice);
         IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
             @Override
             public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+                Log.d(TAG, "onDeviceOpened() server:" + server);
                 MidiDevice device = null;
                 if (server != null) {
                     try {
@@ -423,6 +428,15 @@
         }
     }
 
+    /** @hide */ // for now
+    public void closeBluetoothDevice(@NonNull MidiDevice midiDevice) {
+        try {
+            midiDevice.close();
+        } catch (IOException ex) {
+            Log.e(TAG, "Exception closing BLE-MIDI device" + ex);
+        }
+    }
+
     /** @hide */
     public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
             int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java
index ff4c625..71b1634 100644
--- a/media/java/android/media/tv/AitInfo.java
+++ b/media/java/android/media/tv/AitInfo.java
@@ -17,11 +17,12 @@
 package android.media.tv;
 
 import android.annotation.NonNull;
+import android.media.tv.interactive.TvInteractiveAppInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * AIT info.
+ * AIT (Application Information Table) info.
  * @hide
  */
 public final class AitInfo implements Parcelable {
@@ -50,14 +51,15 @@
     /**
      * Constructs AIT info.
      */
-    public AitInfo(int type, int version) {
+    public AitInfo(@TvInteractiveAppInfo.InteractiveAppType int type, int version) {
         mType = type;
         mVersion = version;
     }
 
     /**
-     * Gets type.
+     * Gets interactive app type.
      */
+    @TvInteractiveAppInfo.InteractiveAppType
     public int getType() {
         return mType;
     }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 98d1599..f438d29 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -31,7 +31,7 @@
 import android.media.AudioDeviceInfo;
 import android.media.AudioFormat.Encoding;
 import android.media.PlaybackParams;
-import android.media.tv.interactive.TvIAppManager;
+import android.media.tv.interactive.TvInteractiveAppManager;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -2318,7 +2318,7 @@
         // @GuardedBy("mMetadataLock")
         private int mVideoHeight;
 
-        private TvIAppManager.Session mIAppSession;
+        private TvInteractiveAppManager.Session mIAppSession;
         private boolean mIAppNotificationEnabled = false;
 
         private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
@@ -2331,11 +2331,11 @@
             mSessionCallbackRecordMap = sessionCallbackRecordMap;
         }
 
-        public TvIAppManager.Session getInteractiveAppSession() {
+        public TvInteractiveAppManager.Session getInteractiveAppSession() {
             return mIAppSession;
         }
 
-        public void setInteractiveAppSession(TvIAppManager.Session iAppSession) {
+        public void setInteractiveAppSession(TvInteractiveAppManager.Session iAppSession) {
             this.mIAppSession = iAppSession;
         }
 
@@ -2593,9 +2593,9 @@
 
         /**
          * Enables interactive app notification.
+         *
          * @param enabled {@code true} if you want to enable interactive app notifications.
          *                {@code false} otherwise.
-         * @hide
          */
         public void setInteractiveAppNotificationEnabled(boolean enabled) {
             if (mToken == null) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 524ba34..9bc7367 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -945,7 +945,13 @@
         }
 
         /**
-         * Notifies AIT info updated.
+         * Informs the app that the AIT (Application Information Table) is updated.
+         *
+         * <p>This method should also be call when
+         * {@link #onSetInteractiveAppNotificationEnabled(boolean)} is called to send the first AIT
+         * info.
+         *
+         * @see #onSetInteractiveAppNotificationEnabled(boolean)
          * @hide
          */
         public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) {
@@ -1198,7 +1204,16 @@
 
         /**
          * Enables or disables interactive app notification.
+         *
+         * <p>This method enables or disables the event detection from the corresponding TV input.
+         * When it's enabled, the TV input service detects events related to interactive app, such
+         * as AIT (Application Information Table) and sends to TvView or the linked TV interactive
+         * app service.
+         *
          * @param enabled {@code true} to enable, {@code false} to disable.
+         *
+         * @see TvView#setInteractiveAppNotificationEnabled(boolean)
+         * @see Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
          * @hide
          */
         public void onSetInteractiveAppNotificationEnabled(boolean enabled) {
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 71f6ad6..d2086c5 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -481,9 +481,18 @@
     }
 
     /**
-     * Enables interactive app notification.
+     * Enables or disables interactive app notification.
+     *
+     * <p>This method enables or disables the event detection from the corresponding TV input. When
+     * it's enabled, the TV input service detects events related to interactive app, such as
+     * AIT (Application Information Table) and sends to TvView or the linked TV interactive app
+     * service.
+     *
      * @param enabled {@code true} if you want to enable interactive app notifications.
      *                {@code false} otherwise.
+     *
+     * @see TvInputService.Session#notifyAitInfoUpdated(android.media.tv.AitInfo)
+     * @see android.media.tv.interactive.TvInteractiveAppView#setTvView(TvView)
      * @hide
      */
     public void setInteractiveAppNotificationEnabled(boolean enabled) {
@@ -1062,12 +1071,12 @@
         }
 
         /**
-         * This is called when the AIT info has been updated.
+         * This is called when the AIT (Application Information Table) info has been updated.
          *
          * @param aitInfo The current AIT info.
          * @hide
          */
-        public void onAitInfoUpdated(String inputId, AitInfo aitInfo) {
+        public void onAitInfoUpdated(@NonNull String inputId, @NonNull AitInfo aitInfo) {
         }
 
         /**
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 1a8fc46..a3e58d1 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -34,7 +34,7 @@
     void onLayoutSurface(int left, int top, int right, int bottom, int seq);
     void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
     void onRemoveBroadcastInfo(int id, int seq);
-    void onSessionStateChanged(int state, int seq);
+    void onSessionStateChanged(int state, int err, int seq);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId, int seq);
     void onTeletextAppStateChanged(int state, int seq);
     void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
similarity index 98%
rename from media/java/android/media/tv/interactive/ITvIAppManager.aidl
rename to media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
index a19a2d2..a8ef095 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl
@@ -31,7 +31,7 @@
  * Interface to the TV interactive app service.
  * @hide
  */
-interface ITvIAppManager {
+interface ITvInteractiveAppManager {
     List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId);
     void prepare(String tiasId, int type, int userId);
     void registerAppLinkInfo(String tiasId, in Bundle info, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
index f4510f6..23be4c6 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl
@@ -27,5 +27,5 @@
     void onInteractiveAppServiceRemoved(in String iAppServiceId);
     void onInteractiveAppServiceUpdated(in String iAppServiceId);
     void onTvInteractiveAppInfoUpdated(in TvInteractiveAppInfo tvIAppInfo);
-    void onStateChanged(in String iAppServiceId, int type, int state);
+    void onStateChanged(in String iAppServiceId, int type, int state, int err);
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
index c1e6622..68fae2d 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl
@@ -23,7 +23,7 @@
 
 /**
  * Top-level interface to a TV Interactive App component (implemented in a Service). It's used for
- * TvIAppManagerService to communicate with TvIAppService.
+ * TvInteractiveAppManagerService to communicate with TvInteractiveAppService.
  * @hide
  */
 oneway interface ITvInteractiveAppService {
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
index f56d3bd..970b943 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl
@@ -17,10 +17,10 @@
 package android.media.tv.interactive;
 
 /**
- * Helper interface for ITvInteractiveAppService to allow the TvIAppService to notify the
- * TvIAppManagerService.
+ * Helper interface for ITvInteractiveAppService to allow the TvInteractiveAppService to notify the
+ * TvInteractiveAppManagerService.
  * @hide
  */
 oneway interface ITvInteractiveAppServiceCallback {
-    void onStateChanged(int type, int state);
+    void onStateChanged(int type, int state, int error);
 }
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index c270424..385f0d4 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -24,7 +24,7 @@
 import android.os.Bundle;
 
 /**
- * Helper interface for ITvInteractiveAppSession to allow TvIAppService to notify the
+ * Helper interface for ITvInteractiveAppSession to allow TvInteractiveAppService to notify the
  * system service when there is a related event.
  * @hide
  */
@@ -33,7 +33,7 @@
     void onLayoutSurface(int left, int top, int right, int bottom);
     void onBroadcastInfoRequest(in BroadcastInfoRequest request);
     void onRemoveBroadcastInfo(int id);
-    void onSessionStateChanged(int state);
+    void onSessionStateChanged(int state, int err);
     void onBiInteractiveAppCreated(in Uri biIAppUri, in String biIAppId);
     void onTeletextAppStateChanged(int state);
     void onCommandRequest(in String cmdType, in Bundle parameters);
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
index 2f96552..e1f535c 100644
--- a/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppInfo.java
@@ -44,7 +44,6 @@
 
 /**
  * This class is used to specify meta information of a TV interactive app.
- * @hide
  */
 public final class TvInteractiveAppInfo implements Parcelable {
     private static final boolean DEBUG = false;
@@ -59,7 +58,7 @@
             INTERACTIVE_APP_TYPE_ATSC,
             INTERACTIVE_APP_TYPE_GINGA,
     })
-    @interface InteractiveAppType {}
+    public @interface InteractiveAppType {}
 
     /** HbbTV interactive app type */
     public static final int INTERACTIVE_APP_TYPE_HBBTV = 0x1;
@@ -77,7 +76,7 @@
             throw new IllegalArgumentException("context cannot be null.");
         }
         Intent intent =
-                new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+                new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
         ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent,
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
         if (resolveInfo == null) {
@@ -171,10 +170,10 @@
         ServiceInfo si = resolveInfo.serviceInfo;
         PackageManager pm = context.getPackageManager();
         try (XmlResourceParser parser =
-                     si.loadXmlMetaData(pm, TvIAppService.SERVICE_META_DATA)) {
+                     si.loadXmlMetaData(pm, TvInteractiveAppService.SERVICE_META_DATA)) {
             if (parser == null) {
                 throw new IllegalStateException(
-                        "No " + TvIAppService.SERVICE_META_DATA
+                        "No " + TvInteractiveAppService.SERVICE_META_DATA
                         + " meta-data found for " + si.name);
             }
 
@@ -194,9 +193,9 @@
             }
 
             TypedArray sa = res.obtainAttributes(attrs,
-                    com.android.internal.R.styleable.TvIAppService);
+                    com.android.internal.R.styleable.TvInteractiveAppService);
             CharSequence[] textArr = sa.getTextArray(
-                    com.android.internal.R.styleable.TvIAppService_supportedTypes);
+                    com.android.internal.R.styleable.TvInteractiveAppService_supportedTypes);
             for (CharSequence cs : textArr) {
                 types.add(cs.toString().toLowerCase());
             }
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
similarity index 87%
rename from media/java/android/media/tv/interactive/TvIAppManager.java
rename to media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index f819438..15a5f823 100755
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -16,6 +16,7 @@
 
 package android.media.tv.interactive;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -52,45 +53,128 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
  * Central system API to the overall TV interactive application framework (TIAF) architecture, which
  * arbitrates interaction between applications and interactive apps.
  */
-@SystemService(Context.TV_IAPP_SERVICE)
-public final class TvIAppManager {
+@SystemService(Context.TV_INTERACTIVE_APP_SERVICE)
+public final class TvInteractiveAppManager {
     // TODO: cleanup and unhide public APIs
-    private static final String TAG = "TvIAppManager";
+    private static final String TAG = "TvInteractiveAppManager";
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = false, prefix = "TV_INTERACTIVE_APP_RTE_STATE_", value = {
-            TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED,
-            TV_INTERACTIVE_APP_RTE_STATE_PREPARING,
-            TV_INTERACTIVE_APP_RTE_STATE_READY,
-            TV_INTERACTIVE_APP_RTE_STATE_ERROR})
-    public @interface TvInteractiveAppRteState {}
+    @IntDef(flag = false, prefix = "SERVICE_STATE_", value = {
+            SERVICE_STATE_UNREALIZED,
+            SERVICE_STATE_PREPARING,
+            SERVICE_STATE_READY,
+            SERVICE_STATE_ERROR})
+    public @interface ServiceState {}
 
     /**
-     * Unrealized state of interactive app RTE.
+     * Unrealized state of interactive app service.
      * @hide
      */
-    public static final int TV_INTERACTIVE_APP_RTE_STATE_UNREALIZED = 1;
+    public static final int SERVICE_STATE_UNREALIZED = 1;
     /**
-     * Preparing state of interactive app RTE.
+     * Preparing state of interactive app service.
      * @hide
      */
-    public static final int TV_INTERACTIVE_APP_RTE_STATE_PREPARING = 2;
+    public static final int SERVICE_STATE_PREPARING = 2;
     /**
-     * Ready state of interactive app RTE.
+     * Ready state of interactive app service.
      * @hide
      */
-    public static final int TV_INTERACTIVE_APP_RTE_STATE_READY = 3;
+    public static final int SERVICE_STATE_READY = 3;
     /**
-     * Error state of interactive app RTE.
+     * Error state of interactive app service.
      * @hide
      */
-    public static final int TV_INTERACTIVE_APP_RTE_STATE_ERROR = 4;
+    public static final int SERVICE_STATE_ERROR = 4;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "INTERACTIVE_APP_STATE_", value = {
+            INTERACTIVE_APP_STATE_STOPPED,
+            INTERACTIVE_APP_STATE_RUNNING,
+            INTERACTIVE_APP_STATE_ERROR})
+    public @interface InteractiveAppState {}
+
+    /**
+     * Stopped (or not started) state of interactive application.
+     * @hide
+     */
+    public static final int INTERACTIVE_APP_STATE_STOPPED = 1;
+    /**
+     * Running state of interactive application.
+     * @hide
+     */
+    public static final int INTERACTIVE_APP_STATE_RUNNING = 2;
+    /**
+     * Error state of interactive application.
+     * @hide
+     */
+    public static final int INTERACTIVE_APP_STATE_ERROR = 3;
+
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = false, prefix = "ERROR_", value = {
+            ERROR_NONE,
+            ERROR_UNKNOWN,
+            ERROR_NOT_SUPPORTED,
+            ERROR_WEAK_SIGNAL,
+            ERROR_RESOURCE_UNAVAILABLE,
+            ERROR_BLOCKED,
+            ERROR_ENCRYPTED,
+            ERROR_UNKNOWN_CHANNEL,
+    })
+    public @interface ErrorCode {}
+
+    /**
+     * No error.
+     * @hide
+     */
+    public static final int ERROR_NONE = 0;
+    /**
+     * Unknown error code.
+     * @hide
+     */
+    public static final int ERROR_UNKNOWN = 1;
+    /**
+     * Error code for an unsupported channel.
+     * @hide
+     */
+    public static final int ERROR_NOT_SUPPORTED = 2;
+    /**
+     * Error code for weak signal.
+     * @hide
+     */
+    public static final int ERROR_WEAK_SIGNAL = 3;
+    /**
+     * Error code when resource (e.g. tuner) is unavailable.
+     * @hide
+     */
+    public static final int ERROR_RESOURCE_UNAVAILABLE = 4;
+    /**
+     * Error code for blocked contents.
+     * @hide
+     */
+    public static final int ERROR_BLOCKED = 5;
+    /**
+     * Error code when the key or module is missing for the encrypted channel.
+     * @hide
+     */
+    public static final int ERROR_ENCRYPTED = 6;
+    /**
+     * Error code when the current channel is an unknown channel.
+     * @hide
+     */
+    public static final int ERROR_UNKNOWN_CHANNEL = 7;
+
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -190,7 +274,7 @@
      */
     public static final String KEY_BACK_URI = "back_uri";
 
-    private final ITvIAppManager mService;
+    private final ITvInteractiveAppManager mService;
     private final int mUserId;
 
     // A mapping from the sequence number of a session to its SessionCallbackRecord.
@@ -209,7 +293,7 @@
     private final ITvInteractiveAppClient mClient;
 
     /** @hide */
-    public TvIAppManager(ITvIAppManager service, int userId) {
+    public TvInteractiveAppManager(ITvInteractiveAppManager service, int userId) {
         mService = service;
         mUserId = userId;
         mClient = new ITvInteractiveAppClient.Stub() {
@@ -285,7 +369,7 @@
 
             @Override
             public void onCommandRequest(
-                    @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                    @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
                     Bundle parameters,
                     int seq) {
                 synchronized (mSessionCallbackRecordMap) {
@@ -383,14 +467,14 @@
             }
 
             @Override
-            public void onSessionStateChanged(int state, int seq) {
+            public void onSessionStateChanged(int state, int err, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
                         Log.e(TAG, "Callback not found for seq " + seq);
                         return;
                     }
-                    record.postSessionStateChanged(state);
+                    record.postSessionStateChanged(state, err);
                 }
             }
 
@@ -458,10 +542,10 @@
             }
 
             @Override
-            public void onStateChanged(String iAppServiceId, int type, int state) {
+            public void onStateChanged(String iAppServiceId, int type, int state, int err) {
                 synchronized (mLock) {
                     for (TvInteractiveAppCallbackRecord record : mCallbackRecords) {
-                        record.postStateChanged(iAppServiceId, type, state);
+                        record.postStateChanged(iAppServiceId, type, state, err);
                     }
                 }
             }
@@ -484,9 +568,10 @@
          * This is called when a TV Interactive App service is added to the system.
          *
          * <p>Normally it happens when the user installs a new TV Interactive App service package
-         * that implements {@link TvIAppService} interface.
+         * that implements {@link TvInteractiveAppService} interface.
          *
          * @param iAppServiceId The ID of the TV Interactive App service.
+         * @hide
          */
         public void onInteractiveAppServiceAdded(@NonNull String iAppServiceId) {
         }
@@ -498,6 +583,7 @@
          * App service package.
          *
          * @param iAppServiceId The ID of the TV Interactive App service.
+         * @hide
          */
         public void onInteractiveAppServiceRemoved(@NonNull String iAppServiceId) {
         }
@@ -509,6 +595,7 @@
          * re-installed or a newer version of the package exists becomes available/unavailable.
          *
          * @param iAppServiceId The ID of the TV Interactive App service.
+         * @hide
          */
         public void onInteractiveAppServiceUpdated(@NonNull String iAppServiceId) {
         }
@@ -524,26 +611,34 @@
          *
          * @param iAppInfo The <code>TvInteractiveAppInfo</code> object that contains new
          *                 information.
+         * @hide
          */
         public void onTvInteractiveAppInfoUpdated(@NonNull TvInteractiveAppInfo iAppInfo) {
         }
 
         /**
          * This is called when the state of the interactive app service is changed.
-         * @hide
+         *
+         * @param type the interactive app type
+         * @param state the current state of the service of the given type
+         * @param err the error code for error state. {@link #ERROR_NONE} is used when the state is
+         *            not {@link #SERVICE_STATE_ERROR}.
          */
         public void onTvInteractiveAppServiceStateChanged(
-                @NonNull String iAppServiceId, int type, @TvInteractiveAppRteState int state) {
+                @NonNull String iAppServiceId,
+                @TvInteractiveAppInfo.InteractiveAppType int type,
+                @ServiceState int state,
+                @ErrorCode int err) {
         }
     }
 
     private static final class TvInteractiveAppCallbackRecord {
         private final TvInteractiveAppCallback mCallback;
-        private final Handler mHandler;
+        private final Executor mExecutor;
 
-        TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Handler handler) {
+        TvInteractiveAppCallbackRecord(TvInteractiveAppCallback callback, Executor executor) {
             mCallback = callback;
-            mHandler = handler;
+            mExecutor = executor;
         }
 
         public TvInteractiveAppCallback getCallback() {
@@ -551,7 +646,7 @@
         }
 
         public void postInteractiveAppServiceAdded(final String iAppServiceId) {
-            mHandler.post(new Runnable() {
+            mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     mCallback.onInteractiveAppServiceAdded(iAppServiceId);
@@ -560,7 +655,7 @@
         }
 
         public void postInteractiveAppServiceRemoved(final String iAppServiceId) {
-            mHandler.post(new Runnable() {
+            mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     mCallback.onInteractiveAppServiceRemoved(iAppServiceId);
@@ -569,7 +664,7 @@
         }
 
         public void postInteractiveAppServiceUpdated(final String iAppServiceId) {
-            mHandler.post(new Runnable() {
+            mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     mCallback.onInteractiveAppServiceUpdated(iAppServiceId);
@@ -578,7 +673,7 @@
         }
 
         public void postTvInteractiveAppInfoUpdated(final TvInteractiveAppInfo iAppInfo) {
-            mHandler.post(new Runnable() {
+            mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
                     mCallback.onTvInteractiveAppInfoUpdated(iAppInfo);
@@ -586,11 +681,12 @@
             });
         }
 
-        public void postStateChanged(String iAppServiceId, int type, int state) {
-            mHandler.post(new Runnable() {
+        public void postStateChanged(String iAppServiceId, int type, int state, int err) {
+            mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onTvInteractiveAppServiceStateChanged(iAppServiceId, type, state);
+                    mCallback.onTvInteractiveAppServiceStateChanged(
+                            iAppServiceId, type, state, err);
                 }
             });
         }
@@ -635,7 +731,6 @@
      *
      * @return List of {@link TvInteractiveAppInfo} for each TV Interactive App service that
      *         describes its meta information.
-     * @hide
      */
     @NonNull
     public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList() {
@@ -699,15 +794,16 @@
      * Registers a {@link TvInteractiveAppCallback}.
      *
      * @param callback A callback used to monitor status of the TV Interactive App services.
-     * @param handler A {@link Handler} that the status change will be delivered to.
+     * @param executor A {@link Executor} that the status change will be delivered to.
      * @hide
      */
     public void registerCallback(
-            @NonNull TvInteractiveAppCallback callback, @NonNull Handler handler) {
+            @NonNull TvInteractiveAppCallback callback,
+            @CallbackExecutor @NonNull Executor executor) {
         Preconditions.checkNotNull(callback);
-        Preconditions.checkNotNull(handler);
+        Preconditions.checkNotNull(executor);
         synchronized (mLock) {
-            mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, handler));
+            mCallbackRecords.add(new TvInteractiveAppCallbackRecord(callback, executor));
         }
     }
 
@@ -742,7 +838,7 @@
 
         private static final long INPUT_SESSION_NOT_RESPONDING_TIMEOUT = 2500;
 
-        private final ITvIAppManager mService;
+        private final ITvInteractiveAppManager mService;
         private final int mUserId;
         private final int mSeq;
         private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
@@ -759,7 +855,7 @@
         private TvInputEventSender mSender;
         private InputChannel mInputChannel;
 
-        private Session(IBinder token, InputChannel channel, ITvIAppManager service,
+        private Session(IBinder token, InputChannel channel, ITvInteractiveAppManager service,
                 int userId, int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
             mToken = token;
             mInputChannel = channel;
@@ -1142,7 +1238,7 @@
         }
 
         /**
-         * Notifies IAPP session when video is available.
+         * Notifies Interactive APP session when video is available.
          */
         public void notifyVideoAvailable() {
             if (mToken == null) {
@@ -1157,7 +1253,7 @@
         }
 
         /**
-         * Notifies IAPP session when video is unavailable.
+         * Notifies Interactive APP session when video is unavailable.
          */
         public void notifyVideoUnavailable(int reason) {
             if (mToken == null) {
@@ -1172,7 +1268,7 @@
         }
 
         /**
-         * Notifies IAPP session when content is allowed.
+         * Notifies Interactive APP session when content is allowed.
          */
         public void notifyContentAllowed() {
             if (mToken == null) {
@@ -1187,7 +1283,7 @@
         }
 
         /**
-         * Notifies IAPP session when content is blocked.
+         * Notifies Interactive APP session when content is blocked.
          */
         public void notifyContentBlocked(TvContentRating rating) {
             if (mToken == null) {
@@ -1478,7 +1574,7 @@
         }
 
         void postCommandRequest(
-                final @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                final @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
                 final Bundle parameters) {
             mHandler.post(new Runnable() {
                 @Override
@@ -1553,11 +1649,11 @@
             });
         }
 
-        void postSessionStateChanged(int state) {
+        void postSessionStateChanged(int state, int err) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mSessionCallback.onSessionStateChanged(mSession, state);
+                    mSessionCallback.onSessionStateChanged(mSession, state, err);
                 }
             });
         }
@@ -1587,28 +1683,28 @@
      */
     public abstract static class SessionCallback {
         /**
-         * This is called after {@link TvIAppManager#createSession} has been processed.
+         * This is called after {@link TvInteractiveAppManager#createSession} has been processed.
          *
-         * @param session A {@link TvIAppManager.Session} instance created. This can be
+         * @param session A {@link TvInteractiveAppManager.Session} instance created. This can be
          *                {@code null} if the creation request failed.
          */
         public void onSessionCreated(@Nullable Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppManager.Session} is released.
+         * This is called when {@link TvInteractiveAppManager.Session} is released.
          * This typically happens when the process hosting the session has crashed or been killed.
          *
-         * @param session the {@link TvIAppManager.Session} instance released.
+         * @param session the {@link TvInteractiveAppManager.Session} instance released.
          */
         public void onSessionReleased(@NonNull Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#layoutSurface} is called to
+         * This is called when {@link TvInteractiveAppService.Session#layoutSurface} is called to
          * change the layout of surface.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          * @param left Left position.
          * @param top Top position.
          * @param right Right position.
@@ -1618,85 +1714,90 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#requestCommand} is called.
+         * This is called when {@link TvInteractiveAppService.Session#requestCommand} is called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          * @param cmdType type of the command.
          * @param parameters parameters of the command.
          */
         public void onCommandRequest(
                 Session session,
-                @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
                 Bundle parameters) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
+         * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          */
         public void onSetVideoBounds(Session session, Rect rect) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
          * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          */
         public void onRequestCurrentChannelUri(Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
          * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          */
         public void onRequestCurrentChannelLcn(Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
          * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          */
         public void onRequestStreamVolume(Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
          * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          */
         public void onRequestTrackInfoList(Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentTvInputId} is
+         * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
          * @hide
          */
         public void onRequestCurrentTvInputId(Session session) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
+         * This is called when {@link TvInteractiveAppService.Session#notifySessionStateChanged} is
+         * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          * @param state the current state.
          */
-        public void onSessionStateChanged(Session session, int state) {
+        public void onSessionStateChanged(
+                Session session,
+                @InteractiveAppState int state,
+                @ErrorCode int err) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#notifyBiInteractiveAppCreated}
+         * This is called when {@link TvInteractiveAppService.Session#notifyBiInteractiveAppCreated}
          * is called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          * @param biIAppUri URI associated this BI interactive app. This is the same URI in
          *                  {@link Session#createBiInteractiveApp(Uri, Bundle)}
          * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive
@@ -1709,11 +1810,11 @@
          * This is called when {@link TvIAppService.Session#notifyTeletextAppStateChanged} is
          * called.
          *
-         * @param session A {@link TvIAppManager.Session} associated with this callback.
+         * @param session A {@link TvInteractiveAppManager.Session} associated with this callback.
          * @param state the current state.
          */
         public void onTeletextAppStateChanged(
-                Session session, @TvIAppManager.TeletextAppState int state) {
+                Session session, @TvInteractiveAppManager.TeletextAppState int state) {
         }
     }
 }
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
similarity index 93%
rename from media/java/android/media/tv/interactive/TvIAppService.java
rename to media/java/android/media/tv/interactive/TvInteractiveAppService.java
index c0ec76b..094aabd 100755
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -65,11 +65,11 @@
 import java.util.List;
 
 /**
- * The TvIAppService class represents a TV interactive applications RTE.
+ * The TvInteractiveAppService class represents a TV interactive applications RTE.
  */
-public abstract class TvIAppService extends Service {
+public abstract class TvInteractiveAppService extends Service {
     private static final boolean DEBUG = false;
-    private static final String TAG = "TvIAppService";
+    private static final String TAG = "TvInteractiveAppService";
 
     private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
 
@@ -83,12 +83,12 @@
      * cannot abuse it.
      */
     public static final String SERVICE_INTERFACE =
-            "android.media.tv.interactive.TvIAppService";
+            "android.media.tv.interactive.TvInteractiveAppService";
 
     /**
-     * Name under which a TvIAppService component publishes information about itself. This
+     * Name under which a TvInteractiveAppService component publishes information about itself. This
      * meta-data must reference an XML resource containing an
-     * <code>&lt;{@link android.R.styleable#TvIAppService tv-interactive-app}&gt;</code>
+     * <code>&lt;{@link android.R.styleable#TvInteractiveAppService tv-interactive-app}&gt;</code>
      * tag.
      */
     public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
@@ -196,8 +196,7 @@
      * Prepares TV Interactive App service for the given type.
      * @hide
      */
-    public void onPrepare(int type) {
-        // TODO: make it abstract when unhide
+    public void onPrepare(@TvInteractiveAppInfo.InteractiveAppType int type) {
     }
 
     /**
@@ -236,20 +235,32 @@
      * @hide
      */
     @Nullable
-    public Session onCreateSession(@NonNull String iAppServiceId, int type) {
-        // TODO: make it abstract when unhide
+    public Session onCreateSession(
+            @NonNull String iAppServiceId,
+            @TvInteractiveAppInfo.InteractiveAppType int type) {
         return null;
     }
 
     /**
-     * Notifies the system when the state of the interactive app has been changed.
-     * @param state the current state
+     * Notifies the system when the state of the interactive app RTE has been changed.
+     *
+     * @param type the interactive app type
+     * @param state the current state of the service of the given type
+     * @param error the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
+     *              used when the state is not
+     *              {@link TvInteractiveAppManager#SERVICE_STATE_ERROR}.
      * @hide
      */
     public final void notifyStateChanged(
-            int type, @TvIAppManager.TvInteractiveAppRteState int state) {
-        mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED,
-                type, state).sendToTarget();
+            @TvInteractiveAppInfo.InteractiveAppType int type,
+            @TvInteractiveAppManager.ServiceState int state,
+            @TvInteractiveAppManager.ErrorCode int error) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = type;
+        args.arg2 = state;
+        args.arg3 = error;
+        mServiceHandler
+                .obtainMessage(ServiceHandler.DO_NOTIFY_RTE_STATE_CHANGED, args).sendToTarget();
     }
 
     /**
@@ -298,6 +309,7 @@
          *
          * @param enable {@code true} if you want to enable the media view. {@code false}
          *            otherwise.
+         * @hide
          */
         public void setMediaViewEnabled(final boolean enable) {
             mHandler.post(new Runnable() {
@@ -319,15 +331,13 @@
         }
 
         /**
-         * Starts TvIAppService session.
-         * @hide
+         * Starts TvInteractiveAppService session.
          */
         public void onStartInteractiveApp() {
         }
 
         /**
-         * Stops TvIAppService session.
-         * @hide
+         * Stops TvInteractiveAppService session.
          */
         public void onStopInteractiveApp() {
         }
@@ -364,6 +374,7 @@
         /**
          * To toggle Digital Teletext Application if there is one in AIT app list.
          * @param enable
+         * @hide
          */
         public void onSetTeletextAppEnabled(boolean enable) {
         }
@@ -438,6 +449,7 @@
          *
          * @param width The width of the media view.
          * @param height The height of the media view.
+         * @hide
          */
         public void onMediaViewSizeChanged(int width, int height) {
         }
@@ -447,6 +459,7 @@
          * implementation can override this method and return its own view.
          *
          * @return a view attached to the media window
+         * @hide
          */
         @Nullable
         public View onCreateMediaView() {
@@ -454,7 +467,7 @@
         }
 
         /**
-         * Releases TvIAppService session.
+         * Releases TvInteractiveAppService session.
          * @hide
          */
         public void onRelease() {
@@ -620,6 +633,7 @@
         /**
          * Requests broadcast related information from the related TV input.
          * @param request the request for broadcast info
+         * @hide
          */
         public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -644,6 +658,7 @@
         /**
          * Remove broadcast information request from the related TV input.
          * @param requestId the ID of the request
+         * @hide
          */
         public void removeBroadcastInfo(final int requestId) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -669,6 +684,7 @@
          * requests a specific command to be processed by the related TV input.
          * @param cmdType type of the specific command
          * @param parameters parameters of the specific command
+         * @hide
          */
         public void requestCommand(
                 @InteractiveAppServiceCommandType String cmdType, Bundle parameters) {
@@ -693,6 +709,7 @@
 
         /**
          * Sets broadcast video bounds.
+         * @hide
          */
         public void setVideoBounds(Rect rect) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -715,6 +732,7 @@
 
         /**
          * Requests the URI of the current channel.
+         * @hide
          */
         public void requestCurrentChannelUri() {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -737,6 +755,7 @@
 
         /**
          * Requests the logic channel number (LCN) of the current channel.
+         * @hide
          */
         public void requestCurrentChannelLcn() {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -759,6 +778,7 @@
 
         /**
          * Requests stream volume.
+         * @hide
          */
         public void requestStreamVolume() {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -781,6 +801,7 @@
 
         /**
          * Requests the list of {@link TvTrackInfo}.
+         * @hide
          */
         public void requestTrackInfoList() {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -829,6 +850,7 @@
         /**
          * requests an advertisement request to be processed by the related TV input.
          * @param request advertisement request
+         * @hide
          */
         public void requestAd(@NonNull final AdRequest request) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -987,10 +1009,15 @@
 
         /**
          * Notifies when the session state is changed.
-         * @param state the current state.
+         *
+         * @param state the current session state.
+         * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE} is
+         *            used when the state is not
+         *            {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
          */
         public void notifySessionStateChanged(
-                @TvIAppManager.TvInteractiveAppRteState int state) {
+                @TvInteractiveAppManager.InteractiveAppState int state,
+                @TvInteractiveAppManager.ErrorCode int err) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -998,10 +1025,10 @@
                     try {
                         if (DEBUG) {
                             Log.d(TAG, "notifySessionStateChanged (state="
-                                    + state + ")");
+                                    + state + "; err=" + err + ")");
                         }
                         if (mSessionCallback != null) {
-                            mSessionCallback.onSessionStateChanged(state);
+                            mSessionCallback.onSessionStateChanged(state, err);
                         }
                     } catch (RemoteException e) {
                         Log.w(TAG, "error in notifySessionStateChanged", e);
@@ -1039,8 +1066,10 @@
         /**
          * Notifies when the digital teletext app state is changed.
          * @param state the current state.
+         * @hide
          */
-        public final void notifyTeletextAppStateChanged(@TvIAppManager.TeletextAppState int state) {
+        public final void notifyTeletextAppStateChanged(
+                @TvInteractiveAppManager.TeletextAppState int state) {
             executeOrPostRunnableOnMainThread(new Runnable() {
                 @MainThread
                 @Override
@@ -1068,7 +1097,7 @@
             if (event instanceof KeyEvent) {
                 KeyEvent keyEvent = (KeyEvent) event;
                 if (keyEvent.dispatch(this, mDispatcherState, this)) {
-                    return TvIAppManager.Session.DISPATCH_HANDLED;
+                    return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
                 }
 
                 // TODO: special handlings of navigation keys and media keys
@@ -1077,20 +1106,20 @@
                 final int source = motionEvent.getSource();
                 if (motionEvent.isTouchEvent()) {
                     if (onTouchEvent(motionEvent)) {
-                        return TvIAppManager.Session.DISPATCH_HANDLED;
+                        return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
                     }
                 } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                     if (onTrackballEvent(motionEvent)) {
-                        return TvIAppManager.Session.DISPATCH_HANDLED;
+                        return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
                     }
                 } else {
                     if (onGenericMotionEvent(motionEvent)) {
-                        return TvIAppManager.Session.DISPATCH_HANDLED;
+                        return TvInteractiveAppManager.Session.DISPATCH_HANDLED;
                     }
                 }
             }
             // TODO: handle overlay view
-            return TvIAppManager.Session.DISPATCH_NOT_HANDLED;
+            return TvInteractiveAppManager.Session.DISPATCH_NOT_HANDLED;
         }
 
         private void initialize(ITvInteractiveAppSessionCallback callback) {
@@ -1443,9 +1472,9 @@
                 }
 
                 int handled = mSessionImpl.dispatchInputEvent(event, this);
-                if (handled != TvIAppManager.Session.DISPATCH_IN_PROGRESS) {
+                if (handled != TvInteractiveAppManager.Session.DISPATCH_IN_PROGRESS) {
                     finishInputEvent(
-                            event, handled == TvIAppManager.Session.DISPATCH_HANDLED);
+                            event, handled == TvInteractiveAppManager.Session.DISPATCH_HANDLED);
                 }
             }
         }
@@ -1457,11 +1486,11 @@
         private static final int DO_NOTIFY_SESSION_CREATED = 2;
         private static final int DO_NOTIFY_RTE_STATE_CHANGED = 3;
 
-        private void broadcastRteStateChanged(int type, int state) {
+        private void broadcastRteStateChanged(int type, int state, int error) {
             int n = mCallbacks.beginBroadcast();
             for (int i = 0; i < n; ++i) {
                 try {
-                    mCallbacks.getBroadcastItem(i).onStateChanged(type, state);
+                    mCallbacks.getBroadcastItem(i).onStateChanged(type, state, error);
                 } catch (RemoteException e) {
                     Log.e(TAG, "error in broadcastRteStateChanged", e);
                 }
@@ -1491,7 +1520,7 @@
                         return;
                     }
                     ITvInteractiveAppSession stub = new ITvInteractiveAppSessionWrapper(
-                            android.media.tv.interactive.TvIAppService.this, sessionImpl, channel);
+                            TvInteractiveAppService.this, sessionImpl, channel);
 
                     SomeArgs someArgs = SomeArgs.obtain();
                     someArgs.arg1 = sessionImpl;
@@ -1519,9 +1548,11 @@
                     return;
                 }
                 case DO_NOTIFY_RTE_STATE_CHANGED: {
-                    int type = msg.arg1;
-                    int state = msg.arg2;
-                    broadcastRteStateChanged(type, state);
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    int type = (int) args.arg1;
+                    int state = (int) args.arg2;
+                    int error = (int) args.arg3;
+                    broadcastRteStateChanged(type, state, error);
                     return;
                 }
                 default: {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 6f99d51..9590d5d 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -27,9 +27,9 @@
 import android.media.tv.TvInputManager;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
-import android.media.tv.interactive.TvIAppManager.Session;
-import android.media.tv.interactive.TvIAppManager.Session.FinishedInputEventCallback;
-import android.media.tv.interactive.TvIAppManager.SessionCallback;
+import android.media.tv.interactive.TvInteractiveAppManager.Session;
+import android.media.tv.interactive.TvInteractiveAppManager.Session.FinishedInputEventCallback;
+import android.media.tv.interactive.TvInteractiveAppManager.SessionCallback;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
@@ -50,7 +50,6 @@
 
 /**
  * Displays contents of interactive TV applications.
- * @hide
  */
 public class TvInteractiveAppView extends ViewGroup {
     private static final String TAG = "TvInteractiveAppView";
@@ -61,7 +60,7 @@
     private static final int UNSET_TVVIEW_SUCCESS = 3;
     private static final int UNSET_TVVIEW_FAIL = 4;
 
-    private final TvIAppManager mTvInteractiveAppManager;
+    private final TvInteractiveAppManager mTvInteractiveAppManager;
     private final Handler mHandler = new Handler();
     private final Object mCallbackLock = new Object();
     private Session mSession;
@@ -141,8 +140,8 @@
         }
         mDefStyleAttr = defStyleAttr;
         resetSurfaceView();
-        mTvInteractiveAppManager = (TvIAppManager) getContext().getSystemService(
-                Context.TV_IAPP_SERVICE);
+        mTvInteractiveAppManager = (TvInteractiveAppManager) getContext().getSystemService(
+                Context.TV_INTERACTIVE_APP_SERVICE);
     }
 
     /**
@@ -152,8 +151,8 @@
      *                 callback.
      */
     public void setCallback(
-            @NonNull TvInteractiveAppCallback callback,
-            @NonNull @CallbackExecutor Executor executor) {
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull TvInteractiveAppCallback callback) {
         synchronized (mCallbackLock) {
             mCallbackExecutor = executor;
             mCallback = callback;
@@ -381,9 +380,16 @@
 
     /**
      * Prepares the interactive application.
+     *
+     * @param iAppServiceId the interactive app service ID, which can be found in
+     *                      {@link TvInteractiveAppInfo#getId()}.
+     *
+     * @see android.media.tv.interactive.TvInteractiveAppManager#getTvInteractiveAppServiceList()
      * @hide
      */
-    public void prepareInteractiveApp(@NonNull String iAppServiceId, int type) {
+    public void prepareInteractiveApp(
+            @NonNull String iAppServiceId,
+            @TvInteractiveAppInfo.InteractiveAppType int type) {
         // TODO: document and handle the cases that this method is called multiple times.
         if (DEBUG) {
             Log.d(TAG, "prepareInteractiveApp");
@@ -552,10 +558,10 @@
 
     /**
      * Sets the TvInteractiveAppView to receive events from TIS. This method links the session of
-     * TvIAppManager to TvInputManager session, so the TIAS can get the TIS events.
+     * TvInteractiveAppManager to TvInputManager session, so the TIAS can get the TIS events.
      *
      * @param tvView the TvView to be linked to this TvInteractiveAppView via linking of Sessions.
-     * @return to be added
+     * @return The result of the operation.
      * @hide
      */
     public int setTvView(@Nullable TvView tvView) {
@@ -583,6 +589,7 @@
     /**
      * To toggle Digital Teletext Application if there is one in AIT app list.
      * @param enable
+     * @hide
      */
     public void setTeletextAppEnabled(boolean enable) {
         if (DEBUG) {
@@ -609,7 +616,7 @@
          */
         public void onCommandRequest(
                 @NonNull String iAppServiceId,
-                @NonNull @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                @NonNull @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
                 @Nullable Bundle parameters) {
         }
 
@@ -617,10 +624,16 @@
          * This is called when the session state is changed.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @param state current session state.
+         * @param state the current state.
+         * @param err the error code for error state. {@link TvInteractiveAppManager#ERROR_NONE}
+         *              is used when the state is not
+         *              {@link TvInteractiveAppManager#INTERACTIVE_APP_STATE_ERROR}.
          * @hide
          */
-        public void onSessionStateChanged(@NonNull String iAppServiceId, int state) {
+        public void onStateChanged(
+                @NonNull String iAppServiceId,
+                @TvInteractiveAppManager.InteractiveAppState int state,
+                @TvInteractiveAppManager.ErrorCode int err) {
         }
 
         /**
@@ -642,13 +655,15 @@
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param state digital teletext app current state.
+         * @hide
          */
         public void onTeletextAppStateChanged(
-                @NonNull String iAppServiceId, @TvIAppManager.TeletextAppState int state) {
+                @NonNull String iAppServiceId,
+                @TvInteractiveAppManager.TeletextAppState int state) {
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#SetVideoBounds} is called.
+         * This is called when {@link TvInteractiveAppService.Session#SetVideoBounds} is called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @hide
@@ -657,7 +672,7 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelUri} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelUri} is
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -667,7 +682,7 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestCurrentChannelLcn} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestCurrentChannelLcn} is
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -677,7 +692,7 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestStreamVolume} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestStreamVolume} is
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -687,7 +702,7 @@
         }
 
         /**
-         * This is called when {@link TvIAppService.Session#RequestTrackInfoList} is
+         * This is called when {@link TvInteractiveAppService.Session#RequestTrackInfoList} is
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
@@ -700,6 +715,7 @@
          * This is called when {@link TvIAppService.Session#RequestCurrentTvInputId} is called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+         * @hide
          */
         public void onRequestCurrentTvInputId(@NonNull String iAppServiceId) {
         }
@@ -801,7 +817,7 @@
         @Override
         public void onCommandRequest(
                 Session session,
-                @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
                 Bundle parameters) {
             if (DEBUG) {
                 Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
@@ -825,9 +841,12 @@
         }
 
         @Override
-        public void onSessionStateChanged(Session session, int state) {
+        public void onSessionStateChanged(
+                Session session,
+                @TvInteractiveAppManager.InteractiveAppState int state,
+                @TvInteractiveAppManager.ErrorCode int err) {
             if (DEBUG) {
-                Log.d(TAG, "onSessionStateChanged (state=" + state +  ")");
+                Log.d(TAG, "onSessionStateChanged (state=" + state + "; err=" + err + ")");
             }
             if (this != mSessionCallback) {
                 Log.w(TAG, "onSessionStateChanged - session not created");
@@ -838,7 +857,7 @@
                     mCallbackExecutor.execute(() -> {
                         synchronized (mCallbackLock) {
                             if (mCallback != null) {
-                                mCallback.onSessionStateChanged(mIAppServiceId, state);
+                                mCallback.onStateChanged(mIAppServiceId, state, err);
                             }
                         }
                     });
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 9c4a83a..3157375 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -1002,7 +1002,7 @@
     private native String nativeGetFrontendHardwareInfo();
     private native int nativeSetMaxNumberOfFrontends(int frontendType, int maxNumber);
     private native int nativeGetMaxNumberOfFrontends(int frontendType);
-
+    private native int nativeRemoveOutputPid(int pid);
     private native Lnb nativeOpenLnbByHandle(int handle);
     private native Lnb nativeOpenLnbByName(String name);
 
@@ -1565,6 +1565,36 @@
     }
 
     /**
+     * Filter out unnecessary PID (packet identifier) from frontend output.
+     *
+     * <p>It is used by the client to remove some video or audio PIDs of other program to reduce the
+     * total amount of recorded TS.
+     *
+     * <p>This API is only supported by Tuner HAL 2.0 or higher. Unsupported version would cause
+     * no-op. Use {@link TunerVersionChecker#getTunerVersion()} to check the version.
+     *
+     * @return result status of the operation. Unsupported version or if current active frontend
+     *         doesn’t support PID filtering out would return {@link #RESULT_UNAVAILABLE}.
+     * @throws IllegalStateException if there is no active frontend currently.
+     */
+    @Result
+    public int removeOutputPid(@IntRange(from = 0) int pid) {
+        mFrontendLock.lock();
+        try {
+            if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_2_0, "Remove output PID")) {
+                return RESULT_UNAVAILABLE;
+            }
+            if (mFrontend == null) {
+                throw new IllegalStateException("frontend is not initialized");
+            }
+            return nativeRemoveOutputPid(pid);
+        } finally {
+            mFrontendLock.unlock();
+        }
+    }
+
+    /**
      * Gets the currently initialized and activated frontend information. To get all the available
      * frontend info on the device, use {@link getAvailableFrontendInfos()}.
      *
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 0a5490d..2e419a6 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -375,7 +375,8 @@
 }
 
 static jlong ImageWriter_init(JNIEnv* env, jobject thiz, jobject weakThiz, jobject jsurface,
-        jint maxImages, jint userFormat, jint userWidth, jint userHeight) {
+        jint maxImages, jint userWidth, jint userHeight, jboolean useSurfaceImageFormatInfo,
+        jint hardwareBufferFormat, jlong dataSpace, jlong ndkUsage) {
     status_t res;
 
     ALOGV("%s: maxImages:%d", __FUNCTION__, maxImages);
@@ -450,7 +451,7 @@
 
     // Query surface format if no valid user format is specified, otherwise, override surface format
     // with user format.
-    if (userFormat == IMAGE_FORMAT_UNKNOWN) {
+    if (useSurfaceImageFormatInfo) {
         if ((res = anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &surfaceFormat)) != OK) {
             ALOGE("%s: Query Surface format failed: %s (%d)", __FUNCTION__, strerror(-res), res);
             jniThrowRuntimeException(env, "Failed to query Surface format");
@@ -458,13 +459,13 @@
         }
     } else {
         // Set consumer buffer format to user specified format
-        PublicFormat publicFormat = static_cast<PublicFormat>(userFormat);
-        int nativeFormat = mapPublicFormatToHalFormat(publicFormat);
-        android_dataspace nativeDataspace = mapPublicFormatToHalDataspace(publicFormat);
-        res = native_window_set_buffers_format(anw.get(), nativeFormat);
+        android_dataspace nativeDataspace = static_cast<android_dataspace>(dataSpace);
+        int userFormat = static_cast<int>(mapHalFormatDataspaceToPublicFormat(
+            hardwareBufferFormat, nativeDataspace));
+        res = native_window_set_buffers_format(anw.get(), hardwareBufferFormat);
         if (res != OK) {
             ALOGE("%s: Unable to configure consumer native buffer format to %#x",
-                    __FUNCTION__, nativeFormat);
+                    __FUNCTION__, hardwareBufferFormat);
             jniThrowRuntimeException(env, "Failed to set Surface format");
             return 0;
         }
@@ -484,15 +485,13 @@
     env->SetIntField(thiz,
             gImageWriterClassInfo.mWriterFormat, reinterpret_cast<jint>(surfaceFormat));
 
-    if (!isFormatOpaque(surfaceFormat)) {
-        res = native_window_set_usage(anw.get(), GRALLOC_USAGE_SW_WRITE_OFTEN);
-        if (res != OK) {
-            ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
-                  __FUNCTION__, static_cast<unsigned int>(GRALLOC_USAGE_SW_WRITE_OFTEN),
-                  surfaceFormat, strerror(-res), res);
-            jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
-            return 0;
-        }
+    res = native_window_set_usage(anw.get(), ndkUsage);
+    if (res != OK) {
+        ALOGE("%s: Configure usage %08x for format %08x failed: %s (%d)",
+              __FUNCTION__, static_cast<unsigned int>(ndkUsage),
+              surfaceFormat, strerror(-res), res);
+        jniThrowRuntimeException(env, "Failed to SW_WRITE_OFTEN configure usage");
+        return 0;
     }
 
     int minUndequeuedBufferCount = 0;
@@ -1093,7 +1092,7 @@
 
 static JNINativeMethod gImageWriterMethods[] = {
     {"nativeClassInit",         "()V",                        (void*)ImageWriter_classInit },
-    {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;IIII)J",
+    {"nativeInit",              "(Ljava/lang/Object;Landroid/view/Surface;IIIZIJJ)J",
                                                               (void*)ImageWriter_init },
     {"nativeClose",              "(J)V",                      (void*)ImageWriter_close },
     {"nativeAttachAndQueueImage",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index e6a79796..41f3a678 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -1622,6 +1622,15 @@
     return mTunerClient->getMaxNumberOfFrontends(static_cast<FrontendType>(type));
 }
 
+jint JTuner::removeOutputPid(int32_t pid) {
+    if (mFeClient == nullptr) {
+        ALOGE("frontend is not initialized");
+        return (jint)Result::INVALID_STATE;
+    }
+
+    return (jint)mFeClient->removeOutputPid(pid);
+}
+
 jobject JTuner::openLnbByHandle(int handle) {
     if (mTunerClient == nullptr) {
         return nullptr;
@@ -4375,6 +4384,11 @@
     return tuner->getMaxNumberOfFrontends(type);
 }
 
+static jint android_media_tv_Tuner_remove_output_pid(JNIEnv *env, jobject thiz, jint pid) {
+    sp<JTuner> tuner = getTuner(env, thiz);
+    return tuner->removeOutputPid(pid);
+}
+
 static jint android_media_tv_Tuner_close_frontend(JNIEnv* env, jobject thiz, jint /* handle */) {
     sp<JTuner> tuner = getTuner(env, thiz);
     return tuner->closeFrontend();
@@ -4694,6 +4708,8 @@
              (void *)android_media_tv_Tuner_set_maximum_frontends },
     { "nativeGetMaxNumberOfFrontends", "(I)I",
             (void *)android_media_tv_Tuner_get_maximum_frontends },
+    { "nativeRemoveOutputPid", "(I)I",
+            (void *)android_media_tv_Tuner_remove_output_pid },
 };
 
 static const JNINativeMethod gFilterMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 502bd6b..e9475dc 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -205,6 +205,7 @@
     Result getFrontendHardwareInfo(string& info);
     jint setMaxNumberOfFrontends(int32_t frontendType, int32_t maxNumber);
     int32_t getMaxNumberOfFrontends(int32_t frontendType);
+    jint removeOutputPid(int32_t pid);
 
     jweak getObject();
 
diff --git a/media/jni/tuner/FrontendClient.cpp b/media/jni/tuner/FrontendClient.cpp
index 0fdd8d8..bea0342 100644
--- a/media/jni/tuner/FrontendClient.cpp
+++ b/media/jni/tuner/FrontendClient.cpp
@@ -143,6 +143,15 @@
     return Result::INVALID_STATE;
 }
 
+Result FrontendClient::removeOutputPid(int32_t pid) {
+    if (mTunerFrontend != nullptr) {
+        Status s = mTunerFrontend->removeOutputPid(pid);
+        return ClientHelper::getServiceSpecificErrorCode(s);
+    }
+
+    return Result::INVALID_STATE;
+}
+
 shared_ptr<ITunerFrontend> FrontendClient::getAidlFrontend() {
     return mTunerFrontend;
 }
diff --git a/media/jni/tuner/FrontendClient.h b/media/jni/tuner/FrontendClient.h
index 77d9098..c6838c8 100644
--- a/media/jni/tuner/FrontendClient.h
+++ b/media/jni/tuner/FrontendClient.h
@@ -120,6 +120,11 @@
      */
     Result getHardwareInfo(string& info);
 
+    /**
+     * Filter out unnecessary PID from frontend output.
+     */
+    Result removeOutputPid(int32_t pid);
+
     int32_t getId();
 
     shared_ptr<ITunerFrontend> getAidlFrontend();
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 2dd9525..4c3b689 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -24,6 +24,7 @@
 import android.bluetooth.BluetoothGattService;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
+import android.media.midi.MidiDevice;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
 import android.media.midi.MidiDeviceStatus;
@@ -63,6 +64,7 @@
             "00002902-0000-1000-8000-00805f9b34fb");
 
     private final BluetoothDevice mBluetoothDevice;
+    private final Context mContext;
     private final BluetoothMidiService mService;
     private final MidiManager mMidiManager;
     private MidiReceiver mOutputReceiver;
@@ -136,6 +138,8 @@
                         // switch to receiving notifications
                         mBluetoothGatt.readCharacteristic(characteristic);
                     }
+
+                    openBluetoothDevice(mBluetoothDevice);
                 }
             } else {
                 Log.e(TAG, "onServicesDiscovered received: " + status);
@@ -249,6 +253,7 @@
 
         mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);
 
+        mContext = context;
         mMidiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
 
         Bundle properties = new Bundle();
@@ -310,6 +315,18 @@
         }
     }
 
+    void openBluetoothDevice(BluetoothDevice btDevice) {
+        Log.d(TAG, "openBluetoothDevice() device: " + btDevice);
+
+        MidiManager midiManager = mContext.getSystemService(MidiManager.class);
+        midiManager.openBluetoothDevice(btDevice,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice device) {
+                    }
+                }, null);
+    }
+
     public IBinder getBinder() {
         return mDeviceServer.asBinder();
     }
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index 45253bb..b150e01 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -28,4 +28,9 @@
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
+
+    <integer-array name="config_supportedDreamComplications">
+    </integer-array>
+    <integer-array name="config_dreamComplicationsEnabledByDefault">
+    </integer-array>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
index 5f2bef7..64a0781 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -31,9 +31,8 @@
     private int mLastDensity;
 
     public InterestingConfigChanges() {
-        this(ActivityInfo.CONFIG_LOCALE
-                | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_SCREEN_LAYOUT
-                | ActivityInfo.CONFIG_ASSETS_PATHS);
+        this(ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_LAYOUT_DIRECTION
+                | ActivityInfo.CONFIG_UI_MODE | ActivityInfo.CONFIG_ASSETS_PATHS);
     }
 
     public InterestingConfigChanges(int flags) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index aed2ec1..3c444f2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -38,6 +38,8 @@
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.settingslib.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -45,9 +47,12 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 public class DreamBackend {
     private static final String TAG = "DreamBackend";
@@ -78,19 +83,41 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({WHILE_CHARGING, WHILE_DOCKED, EITHER, NEVER})
-    public @interface WhenToDream{}
+    public @interface WhenToDream {}
 
     public static final int WHILE_CHARGING = 0;
     public static final int WHILE_DOCKED = 1;
     public static final int EITHER = 2;
     public static final int NEVER = 3;
 
+    /**
+     * The type of dream complications which can be provided by a
+     * {@link com.android.systemui.dreams.ComplicationProvider}.
+     */
+    @IntDef(prefix = {"COMPLICATION_TYPE_"}, value = {
+            COMPLICATION_TYPE_TIME,
+            COMPLICATION_TYPE_DATE,
+            COMPLICATION_TYPE_WEATHER,
+            COMPLICATION_TYPE_AIR_QUALITY,
+            COMPLICATION_TYPE_CAST_INFO
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComplicationType {}
+
+    public static final int COMPLICATION_TYPE_TIME = 1;
+    public static final int COMPLICATION_TYPE_DATE = 2;
+    public static final int COMPLICATION_TYPE_WEATHER = 3;
+    public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
+    public static final int COMPLICATION_TYPE_CAST_INFO = 5;
+
     private final Context mContext;
     private final IDreamManager mDreamManager;
     private final DreamInfoComparator mComparator;
     private final boolean mDreamsEnabledByDefault;
     private final boolean mDreamsActivatedOnSleepByDefault;
     private final boolean mDreamsActivatedOnDockByDefault;
+    private final Set<Integer> mSupportedComplications;
+    private final Set<Integer> mDefaultEnabledComplications;
 
     private static DreamBackend sInstance;
 
@@ -103,17 +130,31 @@
 
     public DreamBackend(Context context) {
         mContext = context.getApplicationContext();
+        final Resources resources = mContext.getResources();
+
         mDreamManager = IDreamManager.Stub.asInterface(
                 ServiceManager.getService(DreamService.DREAM_SERVICE));
         mComparator = new DreamInfoComparator(getDefaultDream());
-        mDreamsEnabledByDefault = mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_dreamsEnabledByDefault);
-        mDreamsActivatedOnSleepByDefault = mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
-        mDreamsActivatedOnDockByDefault = mContext.getResources()
-                .getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
-        mDreamPreviewDefault = mContext.getResources().getDrawable(
+        mDreamsEnabledByDefault = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsEnabledByDefault);
+        mDreamsActivatedOnSleepByDefault = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsActivatedOnSleepByDefault);
+        mDreamsActivatedOnDockByDefault = resources.getBoolean(
+                com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
+        mDreamPreviewDefault = resources.getDrawable(
                 com.android.internal.R.drawable.default_dream_preview);
+
+        mSupportedComplications =
+                Arrays.stream(resources.getIntArray(R.array.config_supportedDreamComplications))
+                        .boxed()
+                        .collect(Collectors.toSet());
+
+        mDefaultEnabledComplications = Arrays.stream(
+                        resources.getIntArray(R.array.config_dreamComplicationsEnabledByDefault))
+                .boxed()
+                // A complication can only be enabled by default if it is also supported.
+                .filter(mSupportedComplications::contains)
+                .collect(Collectors.toSet());
     }
 
     public List<DreamInfo> getDreamInfos() {
@@ -242,7 +283,57 @@
             default:
                 break;
         }
+    }
 
+    /** Gets all complications which have been enabled by the user. */
+    public Set<Integer> getEnabledComplications() {
+        final String enabledComplications = Settings.Secure.getString(
+                mContext.getContentResolver(),
+                Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS);
+
+        if (enabledComplications == null) {
+            return mDefaultEnabledComplications;
+        }
+
+        return parseFromString(enabledComplications);
+    }
+
+    /** Gets all dream complications which are supported on this device. **/
+    public Set<Integer> getSupportedComplications() {
+        return mSupportedComplications;
+    }
+
+    /**
+     * Enables or disables a particular dream complication.
+     *
+     * @param complicationType The dream complication to be enabled/disabled.
+     * @param value            If true, the complication is enabled. Otherwise it is disabled.
+     */
+    public void setComplicationEnabled(@ComplicationType int complicationType, boolean value) {
+        if (!mSupportedComplications.contains(complicationType)) return;
+
+        Set<Integer> enabledComplications = getEnabledComplications();
+        if (value) {
+            enabledComplications.add(complicationType);
+        } else {
+            enabledComplications.remove(complicationType);
+        }
+
+        Settings.Secure.putString(mContext.getContentResolver(),
+                Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS,
+                convertToString(enabledComplications));
+    }
+
+    private static String convertToString(Set<Integer> set) {
+        return set.stream()
+                .map(String::valueOf)
+                .collect(Collectors.joining(","));
+    }
+
+    private static Set<Integer> parseFromString(String string) {
+        return Arrays.stream(string.split(","))
+                .map(Integer::parseInt)
+                .collect(Collectors.toSet());
     }
 
     public boolean isEnabled() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
new file mode 100644
index 0000000..53d4653
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.dream;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import com.android.settingslib.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowSettings;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowSettings.ShadowSecure.class})
+public final class DreamBackendTest {
+    private static final int[] SUPPORTED_DREAM_COMPLICATIONS = {1, 2, 3};
+    private static final int[] DEFAULT_DREAM_COMPLICATIONS = {1, 3, 4};
+
+    @Mock
+    private Context mContext;
+    private DreamBackend mBackend;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mContext.getApplicationContext()).thenReturn(mContext);
+
+        final Resources res = mock(Resources.class);
+        when(mContext.getResources()).thenReturn(res);
+        when(res.getIntArray(R.array.config_supportedDreamComplications)).thenReturn(
+                SUPPORTED_DREAM_COMPLICATIONS);
+        when(res.getIntArray(R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
+                DEFAULT_DREAM_COMPLICATIONS);
+        mBackend = new DreamBackend(mContext);
+    }
+
+    @After
+    public void tearDown() {
+        ShadowSettings.ShadowSecure.reset();
+    }
+
+    @Test
+    public void testSupportedComplications() {
+        assertThat(mBackend.getSupportedComplications()).containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void testGetEnabledDreamComplications_default() {
+        assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+    }
+
+    @Test
+    public void testEnableComplication() {
+        mBackend.setComplicationEnabled(/* complicationType= */ 2, true);
+        assertThat(mBackend.getEnabledComplications()).containsExactly(1, 2, 3);
+    }
+
+    @Test
+    public void testEnableComplication_notSupported() {
+        mBackend.setComplicationEnabled(/* complicationType= */ 5, true);
+        assertThat(mBackend.getEnabledComplications()).containsExactly(1, 3);
+    }
+
+    @Test
+    public void testDisableComplication() {
+        mBackend.setComplicationEnabled(/* complicationType= */ 1, false);
+        assertThat(mBackend.getEnabledComplications()).containsExactly(3);
+    }
+}
+
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
index aa11952..06b6fc8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/BiometricActionDisabledByAdminControllerTest.java
@@ -22,7 +22,6 @@
 
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertSame;
 
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -37,7 +36,6 @@
 import com.android.settingslib.RestrictedLockUtils;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -67,9 +65,8 @@
     }
 
     @Test
-    @Ignore
     public void buttonClicked() {
-        ComponentName componentName = mock(ComponentName.class);
+        ComponentName componentName = new ComponentName("com.android.test", "AThing");
         RestrictedLockUtils.EnforcedAdmin enforcedAdmin = new RestrictedLockUtils.EnforcedAdmin(
                 componentName, new UserHandle(UserHandle.myUserId()));
 
@@ -85,6 +82,6 @@
         assertEquals(Settings.SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS,
                 intentCaptor.getValue().getStringExtra(
                         Settings.EXTRA_SUPERVISOR_RESTRICTED_SETTING_KEY));
-        assertSame(componentName, intentCaptor.getValue().getComponent());
+        assertEquals(componentName.getPackageName(), intentCaptor.getValue().getPackage());
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index c5f027b..7381e05 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1378,9 +1378,6 @@
                 Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
                 GlobalSettingsProto.Sys.STORAGE_CACHE_PERCENTAGE);
         dumpSetting(s, p,
-                Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
-                GlobalSettingsProto.Sys.STORAGE_CACHE_MAX_BYTES);
-        dumpSetting(s, p,
                 Settings.Global.SYS_UIDCPUPOWER,
                 GlobalSettingsProto.Sys.UIDCPUPOWER);
         p.end(sysToken);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index a3f3995..720fb6c 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -462,7 +462,6 @@
                     Settings.Global.SYNC_MANAGER_CONSTANTS,
                     Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
                     Settings.Global.SYS_FREE_STORAGE_LOG_INTERVAL,
-                    Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES,
                     Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE,
                     Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                     Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0bf3648..46e24fa 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -549,6 +549,10 @@
     <!-- Permission required for CTS test - PeopleManagerTest -->
     <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
 
+    <!-- Permissions required for CTS test - TrustTestCases -->
+    <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
+    <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+
     <!-- Permission required for CTS test - CtsGameManagerTestCases -->
     <uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
index 1d2caf9..6345d11 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/navigationbar/RegionSamplingHelper.java
@@ -275,23 +275,27 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println("RegionSamplingHelper:");
-        pw.println("  sampleView isAttached: " + mSampledView.isAttachedToWindow());
-        pw.println("  sampleView isScValid: " + (mSampledView.isAttachedToWindow()
+        dump("", pw);
+    }
+
+    public void dump(String prefix, PrintWriter pw) {
+        pw.println(prefix + "RegionSamplingHelper:");
+        pw.println(prefix + "\tsampleView isAttached: " + mSampledView.isAttachedToWindow());
+        pw.println(prefix + "\tsampleView isScValid: " + (mSampledView.isAttachedToWindow()
                 ? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
                 : "notAttached"));
-        pw.println("  mSamplingEnabled: " + mSamplingEnabled);
-        pw.println("  mSamplingListenerRegistered: " + mSamplingListenerRegistered);
-        pw.println("  mSamplingRequestBounds: " + mSamplingRequestBounds);
-        pw.println("  mRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
-        pw.println("  mLastMedianLuma: " + mLastMedianLuma);
-        pw.println("  mCurrentMedianLuma: " + mCurrentMedianLuma);
-        pw.println("  mWindowVisible: " + mWindowVisible);
-        pw.println("  mWindowHasBlurs: " + mWindowHasBlurs);
-        pw.println("  mWaitingOnDraw: " + mWaitingOnDraw);
-        pw.println("  mRegisteredStopLayer: " + mRegisteredStopLayer);
-        pw.println("  mWrappedStopLayer: " + mWrappedStopLayer);
-        pw.println("  mIsDestroyed: " + mIsDestroyed);
+        pw.println(prefix + "\tmSamplingEnabled: " + mSamplingEnabled);
+        pw.println(prefix + "\tmSamplingListenerRegistered: " + mSamplingListenerRegistered);
+        pw.println(prefix + "\tmSamplingRequestBounds: " + mSamplingRequestBounds);
+        pw.println(prefix + "\tmRegisteredSamplingBounds: " + mRegisteredSamplingBounds);
+        pw.println(prefix + "\tmLastMedianLuma: " + mLastMedianLuma);
+        pw.println(prefix + "\tmCurrentMedianLuma: " + mCurrentMedianLuma);
+        pw.println(prefix + "\tmWindowVisible: " + mWindowVisible);
+        pw.println(prefix + "\tmWindowHasBlurs: " + mWindowHasBlurs);
+        pw.println(prefix + "\tmWaitingOnDraw: " + mWaitingOnDraw);
+        pw.println(prefix + "\tmRegisteredStopLayer: " + mRegisteredStopLayer);
+        pw.println(prefix + "\tmWrappedStopLayer: " + mWrappedStopLayer);
+        pw.println(prefix + "\tmIsDestroyed: " + mIsDestroyed);
     }
 
     public interface SamplingCallback {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index d518cb5..bb7a0a71 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -52,13 +52,14 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.view.RotationPolicy;
-import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.recents.utilities.Utilities;
 import com.android.systemui.shared.recents.utilities.ViewRippler;
+import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 
+import java.io.PrintWriter;
 import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
@@ -450,6 +451,30 @@
         return mDarkIconColor;
     }
 
+    public void dumpLogs(String prefix, PrintWriter pw) {
+        pw.println(prefix + "RotationButtonController:");
+
+        pw.println(String.format(
+                "%s\tmIsRecentsAnimationRunning=%b", prefix, mIsRecentsAnimationRunning));
+        pw.println(String.format("%s\tmHomeRotationEnabled=%b", prefix, mHomeRotationEnabled));
+        pw.println(String.format(
+                "%s\tmLastRotationSuggestion=%d", prefix, mLastRotationSuggestion));
+        pw.println(String.format(
+                "%s\tmPendingRotationSuggestion=%b", prefix, mPendingRotationSuggestion));
+        pw.println(String.format(
+                "%s\tmHoveringRotationSuggestion=%b", prefix, mHoveringRotationSuggestion));
+        pw.println(String.format("%s\tmListenersRegistered=%b", prefix, mListenersRegistered));
+        pw.println(String.format(
+                "%s\tmIsNavigationBarShowing=%b", prefix, mIsNavigationBarShowing));
+        pw.println(String.format("%s\tmBehavior=%d", prefix, mBehavior));
+        pw.println(String.format(
+                "%s\tmSkipOverrideUserLockPrefsOnce=%b", prefix, mSkipOverrideUserLockPrefsOnce));
+        pw.println(String.format(
+                "%s\tmLightIconColor=0x%s", prefix, Integer.toHexString(mLightIconColor)));
+        pw.println(String.format(
+                "%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor)));
+    }
+
     public RotationButton getRotationButton() {
         return mRotationButton;
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index 0340904..b2658c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.keyguard;
 
+import android.util.Log;
+
 /**
  * Defines constants for the Keyguard.
  */
@@ -25,7 +27,7 @@
      * Turns on debugging information for the whole Keyguard. This is very verbose and should only
      * be used temporarily for debugging.
      */
-    public static final boolean DEBUG = false;
+    public static final boolean DEBUG = Log.isLoggable("Keyguard", Log.DEBUG);
     public static final boolean DEBUG_SIM_STATES = true;
     public static final boolean DEBUG_BIOMETRIC_WAKELOCK = true;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 2767904..b3be877 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -122,7 +122,8 @@
                     .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
                     .setRecentTasks(mWMComponent.getRecentTasks())
                     .setCompatUI(Optional.of(mWMComponent.getCompatUI()))
-                    .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()));
+                    .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop()))
+                    .setBackAnimation(mWMComponent.getBackAnimation());
         } else {
             // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
             // is separating this logic into newly creating SystemUITestsFactory.
@@ -142,7 +143,8 @@
                     .setTaskSurfaceHelper(Optional.ofNullable(null))
                     .setRecentTasks(Optional.ofNullable(null))
                     .setCompatUI(Optional.ofNullable(null))
-                    .setDragAndDrop(Optional.ofNullable(null));
+                    .setDragAndDrop(Optional.ofNullable(null))
+                    .setBackAnimation(Optional.ofNullable(null));
         }
         mSysUIComponent = builder.build();
         if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index b235692..bda8e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -36,6 +36,7 @@
 import com.android.wm.shell.ShellCommandHandler;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
@@ -121,6 +122,9 @@
         @BindsInstance
         Builder setDragAndDrop(Optional<DragAndDrop> d);
 
+        @BindsInstance
+        Builder setBackAnimation(Optional<BackAnimation> b);
+
         SysUIComponent build();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b815d4e..b926692 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -24,6 +24,7 @@
 import com.android.wm.shell.ShellInit;
 import com.android.wm.shell.TaskViewFactory;
 import com.android.wm.shell.apppairs.AppPairs;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.compatui.CompatUI;
 import com.android.wm.shell.dagger.TvWMShellModule;
@@ -123,4 +124,7 @@
 
     @WMSingleton
     DragAndDrop getDragAndDrop();
+
+    @WMSingleton
+    Optional<BackAnimation> getBackAnimation();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
index b24d08d..3ae11ff 100644
--- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
+++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java
@@ -54,7 +54,7 @@
     private final View mRootView;
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
             ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
-                | ActivityInfo.CONFIG_SCREEN_LAYOUT | ActivityInfo.CONFIG_ASSETS_PATHS);
+                    | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_ASSETS_PATHS);
     private final FragmentService mManager;
     private final ExtensionFragmentManager mPlugins = new ExtensionFragmentManager();
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index d190dcb..1bef32a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -141,6 +141,7 @@
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -190,6 +191,7 @@
     private final Optional<Pip> mPipOptional;
     private final Optional<LegacySplitScreen> mSplitScreenOptional;
     private final Optional<Recents> mRecentsOptional;
+    private final Optional<BackAnimation> mBackAnimation;
     private final SystemActions mSystemActions;
     private final Handler mHandler;
     private final NavigationBarOverlayController mNavbarOverlayController;
@@ -504,7 +506,8 @@
             AutoHideController mainAutoHideController,
             AutoHideController.Factory autoHideControllerFactory,
             Optional<TelecomManager> telecomManagerOptional,
-            InputMethodManager inputMethodManager) {
+            InputMethodManager inputMethodManager,
+            Optional<BackAnimation> backAnimation) {
         mContext = context;
         mWindowManager = windowManager;
         mAccessibilityManager = accessibilityManager;
@@ -524,6 +527,7 @@
         mPipOptional = pipOptional;
         mSplitScreenOptional = splitScreenOptional;
         mRecentsOptional = recentsOptional;
+        mBackAnimation = backAnimation;
         mSystemActions = systemActions;
         mHandler = mainHandler;
         mNavbarOverlayController = navbarOverlayController;
@@ -629,6 +633,7 @@
 
         mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
         mPipOptional.ifPresent(mNavigationBarView::addPipExclusionBoundsChangeListener);
+        mBackAnimation.ifPresent(mNavigationBarView::registerBackAnimation);
 
         prepareNavigationBarView();
         checkNavBarModes();
@@ -1680,6 +1685,7 @@
         private final AutoHideController.Factory mAutoHideControllerFactory;
         private final Optional<TelecomManager> mTelecomManagerOptional;
         private final InputMethodManager mInputMethodManager;
+        private final Optional<BackAnimation> mBackAnimation;
 
         @Inject
         public Factory(
@@ -1712,7 +1718,8 @@
                 AutoHideController mainAutoHideController,
                 AutoHideController.Factory autoHideControllerFactory,
                 Optional<TelecomManager> telecomManagerOptional,
-                InputMethodManager inputMethodManager) {
+                InputMethodManager inputMethodManager,
+                Optional<BackAnimation> backAnimation) {
             mAssistManagerLazy = assistManagerLazy;
             mAccessibilityManager = accessibilityManager;
             mDeviceProvisionedController = deviceProvisionedController;
@@ -1743,6 +1750,7 @@
             mAutoHideControllerFactory = autoHideControllerFactory;
             mTelecomManagerOptional = telecomManagerOptional;
             mInputMethodManager = inputMethodManager;
+            mBackAnimation = backAnimation;
         }
 
         /** Construct a {@link NavigationBar} */
@@ -1759,7 +1767,7 @@
                     mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
                     mUserTracker, mMainLightBarController, mLightBarControllerFactory,
                     mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
-                    mInputMethodManager);
+                    mInputMethodManager, mBackAnimation);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index a984974..98b49b1 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -59,6 +59,7 @@
 import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
 import java.io.FileDescriptor;
@@ -109,7 +110,8 @@
             DumpManager dumpManager,
             AutoHideController autoHideController,
             LightBarController lightBarController,
-            Optional<Pip> pipOptional) {
+            Optional<Pip> pipOptional,
+            Optional<BackAnimation> backAnimation) {
         mContext = context;
         mHandler = mainHandler;
         mNavigationBarFactory = navigationBarFactory;
@@ -121,7 +123,8 @@
         mTaskbarDelegate = taskbarDelegate;
         mTaskbarDelegate.setDependencies(commandQueue, overviewProxyService,
                 navBarHelper, navigationModeController, sysUiFlagsContainer,
-                dumpManager, autoHideController, lightBarController, pipOptional);
+                dumpManager, autoHideController, lightBarController, pipOptional,
+                backAnimation.orElse(null));
         mIsTablet = isTablet(mContext);
         dumpManager.registerDumpable(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index ac816ba..2dd89f3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -91,6 +91,7 @@
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -1417,6 +1418,10 @@
         pip.removePipExclusionBoundsChangeListener(mPipListener);
     }
 
+    void registerBackAnimation(BackAnimation backAnimation) {
+        mEdgeBackGestureHandler.setBackAnimation(backAnimation);
+    }
+
     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
         pw.print("      " + caption + ": ");
         if (button == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 002dd10..441e79a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.phone.BarTransitions;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
 import java.io.FileDescriptor;
@@ -150,6 +151,7 @@
             mAutoHideController.touchAutoHide();
         }
     };
+    private BackAnimation mBackAnimation;
 
     @Inject
     public TaskbarDelegate(Context context) {
@@ -172,7 +174,8 @@
             SysUiState sysUiState, DumpManager dumpManager,
             AutoHideController autoHideController,
             LightBarController lightBarController,
-            Optional<Pip> pipOptional) {
+            Optional<Pip> pipOptional,
+            BackAnimation backAnimation) {
         // TODO: adding this in the ctor results in a dagger dependency cycle :(
         mCommandQueue = commandQueue;
         mOverviewProxyService = overviewProxyService;
@@ -184,6 +187,7 @@
         mLightBarController = lightBarController;
         mLightBarTransitionsController = createLightBarTransitionsController();
         mPipOptional = pipOptional;
+        mBackAnimation = backAnimation;
     }
 
     // Separated into a method to keep setDependencies() clean/readable.
@@ -233,6 +237,7 @@
         mAutoHideController.setNavigationBar(mAutoHideUiElement);
         mLightBarController.setNavigationBar(mLightBarTransitionsController);
         mPipOptional.ifPresent(this::addPipExclusionBoundsChangeListener);
+        mEdgeBackGestureHandler.setBackAnimation(mBackAnimation);
         mInitialized = true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index ab48a28..9e350ee 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -79,6 +79,7 @@
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
 import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.wm.shell.back.BackAnimation;
 
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
@@ -231,6 +232,7 @@
     private InputChannelCompat.InputEventReceiver mInputEventReceiver;
 
     private NavigationEdgeBackPlugin mEdgeBackPlugin;
+    private BackAnimation mBackAnimation;
     private int mLeftInset;
     private int mRightInset;
     private int mSysUiFlags;
@@ -494,7 +496,7 @@
                     Choreographer.getInstance(), this::onInputEvent);
 
             // Add a nav bar panel window
-            setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+            setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation));
             mPluginManager.addPluginListener(
                     this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
         }
@@ -509,7 +511,7 @@
 
     @Override
     public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) {
-        setEdgeBackPlugin(new NavigationBarEdgePanel(mContext));
+        setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation));
     }
 
     private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
@@ -930,6 +932,10 @@
         proto.edgeBackGestureHandler.allowGesture = mAllowGesture;
     }
 
+    public void setBackAnimation(BackAnimation backAnimation) {
+        mBackAnimation = backAnimation;
+    }
+
     /**
      * Injectable instance to create a new EdgeBackGestureHandler.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index 8d1dfc8..c18209d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -57,6 +57,7 @@
 import com.android.systemui.plugins.NavigationEdgeBackPlugin;
 import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.wm.shell.back.BackAnimation;
 
 import java.io.PrintWriter;
 import java.util.concurrent.Executor;
@@ -277,11 +278,14 @@
                 }
             };
     private BackCallback mBackCallback;
+    private final BackAnimation mBackAnimation;
 
-    public NavigationBarEdgePanel(Context context) {
+    public NavigationBarEdgePanel(Context context,
+            BackAnimation backAnimation) {
         super(context);
 
         mWindowManager = context.getSystemService(WindowManager.class);
+        mBackAnimation = backAnimation;
         mVibratorHelper = Dependency.get(VibratorHelper.class);
 
         mDensity = context.getResources().getDisplayMetrics().density;
@@ -459,6 +463,9 @@
 
     @Override
     public void onMotionEvent(MotionEvent event) {
+        if (mBackAnimation != null) {
+            mBackAnimation.onBackMotion(event);
+        }
         if (mVelocityTracker == null) {
             mVelocityTracker = VelocityTracker.obtain();
         }
@@ -866,6 +873,9 @@
             // Whenever the trigger back state changes the existing translation animation should be
             // cancelled
             mTranslationAnimation.cancel();
+            if (mBackAnimation != null) {
+                mBackAnimation.setTriggerBack(triggerBack);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
new file mode 100644
index 0000000..03b978e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/SectionHeaderVisibilityProvider.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A class which keeps track of whether section headers should be shown in the notification shade.
+ *
+ * (In an ideal world, this would directly monitor the state of the keyguard and invalidate the
+ * pipeline to show/hide headers, but the KeyguardController already invalidates the pipeline when
+ * the keyguard's state changes. Instead of having both classes monitor for state changes and ending
+ * up with duplicate runs of the pipeline, we let the KeyguardController update the header
+ * visibility when it invalidates, and we just store that state here.)
+ */
+@SysUISingleton
+class SectionHeaderVisibilityProvider @Inject constructor() {
+    var sectionHeadersVisible = true
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 09ae7eb..87e531c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -260,7 +260,8 @@
         }
         events.sort(mEventComparator);
 
-        mLogger.logEmitBatch(batch.mGroupKey);
+        long batchAge = mClock.uptimeMillis() - batch.mCreatedTimestamp;
+        mLogger.logEmitBatch(batch.mGroupKey, batch.mMembers.size(), batchAge);
 
         mHandler.onNotificationBatchPosted(events);
     }
@@ -337,6 +338,6 @@
         void onNotificationBatchPosted(List<CoalescedEvent> events);
     }
 
-    private static final int MIN_GROUP_LINGER_DURATION = 50;
+    private static final int MIN_GROUP_LINGER_DURATION = 200;
     private static final int MAX_GROUP_LINGER_DURATION = 500;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index d4d5b64..211e374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -32,11 +32,13 @@
         })
     }
 
-    fun logEmitBatch(groupKey: String) {
+    fun logEmitBatch(groupKey: String, batchSize: Int, batchAgeMs: Long) {
         buffer.log(TAG, LogLevel.DEBUG, {
             str1 = groupKey
+            int1 = batchSize
+            long1 = batchAgeMs
         }, {
-            "Emitting event batch for group $str1"
+            "Emitting batch for group $str1 size=$int1 age=${long1}ms"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index e59f4a6..ba88ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.dagger.PeopleHeader
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
 import javax.inject.Inject
@@ -48,18 +50,36 @@
 
     val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) {
         override fun isInSection(entry: ListEntry): Boolean =
-                isConversation(entry.representativeEntry!!)
+                isConversation(entry)
         override fun getHeaderNodeController() =
                 // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
                 if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
     }
 
+    val comparator = object : NotifComparator("People") {
+        override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+            assert(entry1.section === entry2.section)
+            if (entry1.section?.sectioner !== sectioner) {
+                return 0
+            }
+            val type1 = getPeopleType(entry1)
+            val type2 = getPeopleType(entry2)
+            return type2.compareTo(type1)
+        }
+    }
+
     override fun attach(pipeline: NotifPipeline) {
         pipeline.addPromoter(notificationPromoter)
     }
 
-    private fun isConversation(entry: NotificationEntry): Boolean =
-        peopleNotificationIdentifier.getPeopleNotificationType(entry) != TYPE_NON_PERSON
+    private fun isConversation(entry: ListEntry): Boolean =
+        getPeopleType(entry) != TYPE_NON_PERSON
+
+    @PeopleNotificationType
+    private fun getPeopleType(entry: ListEntry): Int =
+        entry.representativeEntry?.let {
+            peopleNotificationIdentifier.getPeopleNotificationType(it)
+        } ?: TYPE_NON_PERSON
 
     companion object {
         private const val TAG = "ConversationCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
index 33005b3..733be9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -36,6 +36,8 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -48,7 +50,8 @@
 import javax.inject.Inject;
 
 /**
- * Filters low priority and privacy-sensitive notifications from the lockscreen.
+ * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
+ * headers on the lockscreen.
  */
 @CoordinatorScope
 public class KeyguardCoordinator implements Coordinator {
@@ -62,6 +65,7 @@
     private final StatusBarStateController mStatusBarStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final HighPriorityProvider mHighPriorityProvider;
+    private final SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
 
     private boolean mHideSilentNotificationsOnLockscreen;
 
@@ -74,7 +78,8 @@
             BroadcastDispatcher broadcastDispatcher,
             StatusBarStateController statusBarStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            HighPriorityProvider highPriorityProvider) {
+            HighPriorityProvider highPriorityProvider,
+            SectionHeaderVisibilityProvider sectionHeaderVisibilityProvider) {
         mContext = context;
         mMainHandler = mainThreadHandler;
         mKeyguardStateController = keyguardStateController;
@@ -83,6 +88,7 @@
         mStatusBarStateController = statusBarStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mHighPriorityProvider = highPriorityProvider;
+        mSectionHeaderVisibilityProvider = sectionHeaderVisibilityProvider;
     }
 
     @Override
@@ -214,6 +220,8 @@
     }
 
     private void invalidateListFromFilter(String reason) {
+        mSectionHeaderVisibilityProvider.setSectionHeadersVisible(
+                mStatusBarStateController.getState() != StatusBarState.KEYGUARD);
         mNotifFilter.invalidateList();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 757fb5a..850cb4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import java.io.FileDescriptor
 import java.io.PrintWriter
@@ -63,6 +64,7 @@
 
     private val mCoordinators: MutableList<Coordinator> = ArrayList()
     private val mOrderedSections: MutableList<NotifSectioner> = ArrayList()
+    private val mOrderedComparators: MutableList<NotifComparator> = ArrayList()
 
     /**
      * Creates all the coordinators.
@@ -117,6 +119,9 @@
         mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
         mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
         mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
+
+        // Manually add ordered comparators
+        mOrderedComparators.add(conversationCoordinator.comparator)
     }
 
     /**
@@ -128,6 +133,7 @@
             c.attach(pipeline)
         }
         pipeline.setSections(mOrderedSections)
+        pipeline.setComparators(mOrderedComparators)
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
index 0d150ed..f7bbd28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifComparator.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.collection.listbuilder.pluggable;
 
+import androidx.annotation.NonNull;
+
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 
@@ -39,5 +41,5 @@
      * @return a negative integer, zero, or a positive integer as the first argument is less than
      *      equal to, or greater than the second (same as standard Comparator<> interface).
      */
-    public abstract int compare(ListEntry o1, ListEntry o2);
+    public abstract int compare(@NonNull ListEntry o1, @NonNull ListEntry o2);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index f13470e..607500e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.collection.render
 
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -35,6 +36,7 @@
 class NodeSpecBuilder(
     private val mediaContainerController: MediaContainerController,
     private val sectionsFeatureManager: NotificationSectionsFeatureManager,
+    private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
     private val viewBarn: NotifViewBarn
 ) {
     fun buildNodeSpec(
@@ -51,6 +53,7 @@
 
         var currentSection: NotifSection? = null
         val prevSections = mutableSetOf<NotifSection?>()
+        val showHeaders = sectionHeaderVisibilityProvider.sectionHeadersVisible
 
         for (entry in notifList) {
             val section = entry.section!!
@@ -61,7 +64,7 @@
 
             // If this notif begins a new section, first add the section's header view
             if (section != currentSection) {
-                if (section.headerController != currentSection?.headerController) {
+                if (section.headerController != currentSection?.headerController && showHeaders) {
                     section.headerController?.let { headerController ->
                         root.children.add(NodeSpecImpl(root, headerController))
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index ad97392..4847072 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.view.View
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.ListEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -38,13 +39,15 @@
     @Assisted private val stackController: NotifStackController,
     mediaContainerController: MediaContainerController,
     featureManager: NotificationSectionsFeatureManager,
+    sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
     logger: ShadeViewDifferLogger,
     private val viewBarn: NotifViewBarn
 ) {
     // We pass a shim view here because the listContainer may not actually have a view associated
     // with it and the differ never actually cares about the root node's view.
     private val rootController = RootNodeController(listContainer, View(context))
-    private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager, viewBarn)
+    private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager,
+            sectionHeaderVisibilityProvider, viewBarn)
     private val viewDiffer = ShadeViewDiffer(rootController, logger)
 
     /** Method for attaching this manager to the pipeline. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 5d6e807..aff73e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -349,6 +349,9 @@
     }
 
     private void updateShelfIcons() {
+        if (mShelfIcons == null) {
+            return;
+        }
         updateIconsForLayout(entry -> entry.getIcons().getShelfIcon(), mShelfIcons,
                 true /* showAmbient */,
                 true /* showLowPriority */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 81e5b58..016b953 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -46,6 +46,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.Fragment;
 import android.app.StatusBarManager;
@@ -211,8 +212,10 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -4920,33 +4923,53 @@
 
     private class DebugDrawable extends Drawable {
 
+        private final Set<Integer> mDebugTextUsedYPositions = new HashSet<>();
+        private final Paint mDebugPaint = new Paint();
+
         @Override
-        public void draw(Canvas canvas) {
-            Paint p = new Paint();
-            p.setColor(Color.RED);
-            p.setStrokeWidth(2);
-            p.setStyle(Paint.Style.STROKE);
-            canvas.drawLine(0, getMaxPanelHeight(), mView.getWidth(), getMaxPanelHeight(), p);
-            p.setTextSize(24);
-            if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, p);
-            p.setColor(Color.BLUE);
-            canvas.drawLine(0, getExpandedHeight(), mView.getWidth(), getExpandedHeight(), p);
-            p.setColor(Color.GREEN);
-            canvas.drawLine(0, calculatePanelHeightQsExpanded(), mView.getWidth(),
-                    calculatePanelHeightQsExpanded(), p);
-            p.setColor(Color.YELLOW);
-            canvas.drawLine(0, calculatePanelHeightShade(), mView.getWidth(),
-                    calculatePanelHeightShade(), p);
-            p.setColor(Color.MAGENTA);
-            canvas.drawLine(
-                    0, calculateNotificationsTopPadding(), mView.getWidth(),
-                    calculateNotificationsTopPadding(), p);
-            p.setColor(Color.CYAN);
+        public void draw(@NonNull Canvas canvas) {
+            mDebugTextUsedYPositions.clear();
+
+            mDebugPaint.setColor(Color.RED);
+            mDebugPaint.setStrokeWidth(2);
+            mDebugPaint.setStyle(Paint.Style.STROKE);
+            mDebugPaint.setTextSize(24);
+            if (mHeaderDebugInfo != null) canvas.drawText(mHeaderDebugInfo, 50, 100, mDebugPaint);
+
+            drawDebugInfo(canvas, getMaxPanelHeight(), Color.RED, "getMaxPanelHeight()");
+            drawDebugInfo(canvas, (int) getExpandedHeight(), Color.BLUE, "getExpandedHeight()");
+            drawDebugInfo(canvas, calculatePanelHeightQsExpanded(), Color.GREEN,
+                    "calculatePanelHeightQsExpanded()");
+            drawDebugInfo(canvas, calculatePanelHeightShade(), Color.YELLOW,
+                    "calculatePanelHeightShade()");
+            drawDebugInfo(canvas, (int) calculateNotificationsTopPadding(), Color.MAGENTA,
+                    "calculateNotificationsTopPadding()");
+            drawDebugInfo(canvas, mClockPositionResult.clockY, Color.GRAY,
+                    "mClockPositionResult.clockY");
+
+            mDebugPaint.setColor(Color.CYAN);
             canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(),
-                    mNotificationStackScrollLayoutController.getTopPadding(), p);
-            p.setColor(Color.GRAY);
-            canvas.drawLine(0, mClockPositionResult.clockY, mView.getWidth(),
-                    mClockPositionResult.clockY, p);
+                    mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint);
+        }
+
+        private void drawDebugInfo(Canvas canvas, int y, int color, String label) {
+            mDebugPaint.setColor(color);
+            canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(),
+                    /* stopY= */ y, mDebugPaint);
+            canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint);
+        }
+
+        private int computeDebugYTextPosition(int lineY) {
+            if (lineY - mDebugPaint.getTextSize() < 0) {
+                // Avoiding drawing out of bounds
+                lineY += mDebugPaint.getTextSize();
+            }
+            int textY = lineY;
+            while (mDebugTextUsedYPositions.contains(textY)) {
+                textY = (int) (textY + mDebugPaint.getTextSize());
+            }
+            mDebugTextUsedYPositions.add(textY);
+            return textY;
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index 1030bfd..33f2140 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -127,10 +127,12 @@
         int rotationLockSetting =
                 mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
         if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            // This should not happen. Device states that have an ignored setting, should also
+            // specify a fallback device state which is not ignored.
             // We won't handle this device state. The same rotation lock setting as before should
             // apply and any changes to the rotation lock setting will be written for the previous
             // valid device state.
-            Log.v(TAG, "Ignoring new device state: " + state);
+            Log.w(TAG, "Missing fallback. Ignoring new device state: " + state);
             return;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
index a418c74..bec5fc8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingsManager.java
@@ -52,12 +52,14 @@
     private final Handler mMainHandler = Handler.getMain();
     private final Set<DeviceStateRotationLockSettingsListener> mListeners = new HashSet<>();
     private SparseIntArray mDeviceStateRotationLockSettings;
+    private SparseIntArray mDeviceStateRotationLockFallbackSettings;
 
     private DeviceStateRotationLockSettingsManager(Context context) {
         mContentResolver = context.getContentResolver();
         mDeviceStateRotationLockDefaults =
                 context.getResources()
                         .getStringArray(R.array.config_perDeviceStateRotationLockDefaults);
+        loadDefaults();
         initializeInMemoryMap();
         listenForSettingsChange(context);
     }
@@ -114,6 +116,11 @@
 
     /** Updates the rotation lock setting for a specified device state. */
     public void updateSetting(int deviceState, boolean rotationLocked) {
+        if (mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState) >= 0) {
+            // The setting for this device state is IGNORED, and has a fallback device state.
+            // The setting for that fallback device state should be the changed in this case.
+            deviceState = mDeviceStateRotationLockFallbackSettings.get(deviceState);
+        }
         mDeviceStateRotationLockSettings.put(
                 deviceState,
                 rotationLocked
@@ -123,16 +130,37 @@
     }
 
     /**
-     * Returns the {@link DeviceStateRotationLockSetting} for the given device state. If no setting
-     * is specified for this device state, it will return {@link
+     * Returns the {@link Settings.Secure.DeviceStateRotationLockSetting} for the given device
+     * state.
+     *
+     * <p>If the setting for this device state is {@link DEVICE_STATE_ROTATION_LOCK_IGNORED}, it
+     * will return the setting for the fallback device state.
+     *
+     * <p>If no fallback is specified for this device state, it will return {@link
      * DEVICE_STATE_ROTATION_LOCK_IGNORED}.
      */
     @Settings.Secure.DeviceStateRotationLockSetting
     public int getRotationLockSetting(int deviceState) {
-        return mDeviceStateRotationLockSettings.get(
-                deviceState, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        int rotationLockSetting = mDeviceStateRotationLockSettings.get(
+                deviceState, /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+            rotationLockSetting = getFallbackRotationLockSetting(deviceState);
+        }
+        return rotationLockSetting;
     }
 
+    private int getFallbackRotationLockSetting(int deviceState) {
+        int indexOfFallbackState = mDeviceStateRotationLockFallbackSettings.indexOfKey(deviceState);
+        if (indexOfFallbackState < 0) {
+            Log.w(TAG, "Setting is ignored, but no fallback was specified.");
+            return DEVICE_STATE_ROTATION_LOCK_IGNORED;
+        }
+        int fallbackState = mDeviceStateRotationLockFallbackSettings.valueAt(indexOfFallbackState);
+        return mDeviceStateRotationLockSettings.get(fallbackState,
+                /* valueIfKeyNotFound= */ DEVICE_STATE_ROTATION_LOCK_IGNORED);
+    }
+
+
     /** Returns true if the rotation is locked for the current device state */
     public boolean isRotationLocked(int deviceState) {
         return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
@@ -223,21 +251,30 @@
     }
 
     private void loadDefaults() {
-        if (mDeviceStateRotationLockDefaults.length == 0) {
-            Log.w(TAG, "Empty default settings");
-            mDeviceStateRotationLockSettings = new SparseIntArray(/* initialCapacity= */ 0);
-            return;
-        }
-        mDeviceStateRotationLockSettings =
-                new SparseIntArray(mDeviceStateRotationLockDefaults.length);
-        for (String serializedDefault : mDeviceStateRotationLockDefaults) {
-            String[] entry = serializedDefault.split(SEPARATOR_REGEX);
+        mDeviceStateRotationLockSettings = new SparseIntArray(
+                mDeviceStateRotationLockDefaults.length);
+        mDeviceStateRotationLockFallbackSettings = new SparseIntArray(1);
+        for (String entry : mDeviceStateRotationLockDefaults) {
+            String[] values = entry.split(SEPARATOR_REGEX);
             try {
-                int key = Integer.parseInt(entry[0]);
-                int value = Integer.parseInt(entry[1]);
-                mDeviceStateRotationLockSettings.put(key, value);
+                int deviceState = Integer.parseInt(values[0]);
+                int rotationLockSetting = Integer.parseInt(values[1]);
+                if (rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_IGNORED) {
+                    if (values.length == 3) {
+                        int fallbackDeviceState = Integer.parseInt(values[2]);
+                        mDeviceStateRotationLockFallbackSettings.put(deviceState,
+                                fallbackDeviceState);
+                    } else {
+                        Log.w(TAG,
+                                "Rotation lock setting is IGNORED, but values have unexpected "
+                                        + "size of "
+                                        + values.length);
+                    }
+                }
+                mDeviceStateRotationLockSettings.put(deviceState, rotationLockSetting);
             } catch (NumberFormatException e) {
-                Log.wtf(TAG, "Error deserializing default settings", e);
+                Log.wtf(TAG, "Error parsing settings entry. Entry was: " + entry, e);
+                return;
             }
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 3e8e874..73d2b0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.pip.Pip;
 
 import org.junit.After;
@@ -92,7 +93,8 @@
                         mock(DumpManager.class),
                         mock(AutoHideController.class),
                         mock(LightBarController.class),
-                        Optional.of(mock(Pip.class))));
+                        Optional.of(mock(Pip.class)),
+                        Optional.of(mock(BackAnimation.class))));
         initializeNavigationBars();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 5003013..9ca898b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -95,6 +95,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 
@@ -105,7 +106,6 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
 
 import java.util.Optional;
 
@@ -383,7 +383,8 @@
                 mAutoHideController,
                 mAutoHideControllerFactory,
                 Optional.of(mTelecomManager),
-                mInputMethodManager);
+                mInputMethodManager,
+                Optional.of(mock(BackAnimation.class)));
         return spy(factory.create(context));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index b832577..25dd23a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -1968,7 +1968,7 @@
         }
 
         @Override
-        public int compare(ListEntry o1, ListEntry o2) {
+        public int compare(@NonNull ListEntry o1, @NonNull ListEntry o2) {
             boolean contains1 = mPreferredPackages.contains(
                     o1.getRepresentativeEntry().getSbn().getPackageName());
             boolean contains2 = mPreferredPackages.contains(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index a46b440..8deac94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -24,18 +24,24 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
 import com.android.systemui.statusbar.notification.collection.render.NodeController
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.withArgCaptor
+import com.google.common.truth.Truth.assertThat
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
@@ -47,12 +53,15 @@
     // captured listeners and pluggables:
     private lateinit var promoter: NotifPromoter
     private lateinit var peopleSectioner: NotifSectioner
+    private lateinit var peopleComparator: NotifComparator
 
     @Mock private lateinit var pipeline: NotifPipeline
     @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier
     @Mock private lateinit var channel: NotificationChannel
     @Mock private lateinit var headerController: NodeController
     private lateinit var entry: NotificationEntry
+    private lateinit var entryA: NotificationEntry
+    private lateinit var entryB: NotificationEntry
 
     private lateinit var coordinator: ConversationCoordinator
 
@@ -70,8 +79,15 @@
         }
 
         peopleSectioner = coordinator.sectioner
+        peopleComparator = coordinator.comparator
 
         entry = NotificationEntryBuilder().setChannel(channel).build()
+
+        val section = NotifSection(peopleSectioner, 0)
+        entryA = NotificationEntryBuilder().setChannel(channel)
+            .setSection(section).setTag("A").build()
+        entryB = NotificationEntryBuilder().setChannel(channel)
+            .setSection(section).setTag("B").build()
     }
 
     @Test
@@ -90,4 +106,36 @@
         assertTrue(peopleSectioner.isInSection(entry))
         assertFalse(peopleSectioner.isInSection(NotificationEntryBuilder().build()))
     }
+
+    @Test
+    fun testComparatorIgnoresFromOtherSection() {
+        val e1 = NotificationEntryBuilder().setId(1).setChannel(channel).build()
+        val e2 = NotificationEntryBuilder().setId(2).setChannel(channel).build()
+
+        // wrong section -- never classify
+        assertThat(peopleComparator.compare(e1, e2)).isEqualTo(0)
+        verify(peopleNotificationIdentifier, never()).getPeopleNotificationType(any())
+    }
+
+    @Test
+    fun testComparatorPutsImportantPeopleFirst() {
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
+            .thenReturn(TYPE_IMPORTANT_PERSON)
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
+            .thenReturn(TYPE_PERSON)
+
+        // only put people notifications in this section
+        assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1)
+    }
+
+    @Test
+    fun testComparatorEquatesPeopleWithSameType() {
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
+            .thenReturn(TYPE_PERSON)
+        whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB))
+            .thenReturn(TYPE_PERSON)
+
+        // only put people notifications in this section
+        assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0)
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
index 917c049..d094749 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -41,6 +41,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider;
 import com.android.systemui.statusbar.notification.collection.GroupEntry;
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -70,6 +71,7 @@
     @Mock private StatusBarStateController mStatusBarStateController;
     @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock private HighPriorityProvider mHighPriorityProvider;
+    @Mock private SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider;
     @Mock private NotifPipeline mNotifPipeline;
 
     private NotificationEntry mEntry;
@@ -81,7 +83,7 @@
         KeyguardCoordinator keyguardCoordinator = new KeyguardCoordinator(
                 mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
                 mBroadcastDispatcher, mStatusBarStateController,
-                mKeyguardUpdateMonitor, mHighPriorityProvider);
+                mKeyguardUpdateMonitor, mHighPriorityProvider, mSectionHeaderVisibilityProvider);
 
         mEntry = new NotificationEntryBuilder()
                 .setUser(new UserHandle(NOTIF_USER_ID))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index f773810..4e309d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -19,6 +19,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
+import com.android.systemui.statusbar.notification.SectionHeaderVisibilityProvider
 import com.android.systemui.statusbar.notification.collection.GroupEntry
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.ListEntry
@@ -43,6 +44,7 @@
 
     private val mediaContainerController: MediaContainerController = mock()
     private val sectionsFeatureManager: NotificationSectionsFeatureManager = mock()
+    private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
     private val viewBarn: NotifViewBarn = mock()
 
     private var rootController: NodeController = buildFakeController("rootController")
@@ -72,11 +74,13 @@
             fakeViewBarn.getViewByEntry(it.getArgument(0))
         }
 
-        specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager, viewBarn)
+        specBuilder = NodeSpecBuilder(mediaContainerController, sectionsFeatureManager,
+                sectionHeaderVisibilityProvider, viewBarn)
     }
 
     @Test
     fun testMultipleSectionsWithSameController() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 listOf(
                         notif(0, section0),
@@ -95,6 +99,7 @@
 
     @Test(expected = RuntimeException::class)
     fun testMultipleSectionsWithSameControllerNonConsecutive() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 listOf(
                         notif(0, section0),
@@ -108,6 +113,7 @@
 
     @Test
     fun testSimpleMapping() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
             // GIVEN a simple flat list of notifications all in the same headerless section
             listOf(
@@ -129,6 +135,7 @@
 
     @Test
     fun testSimpleMappingWithMedia() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         // WHEN media controls are enabled
         whenever(sectionsFeatureManager.isMediaControlsEnabled()).thenReturn(true)
 
@@ -154,6 +161,8 @@
 
     @Test
     fun testHeaderInjection() {
+        // WHEN section headers are supposed to be visible
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a flat list of notifications, spread across three sections
                 listOf(
@@ -177,7 +186,31 @@
     }
 
     @Test
+    fun testHeaderSuppression() {
+        // WHEN section headers are supposed to be hidden
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(false)
+        checkOutput(
+                // GIVEN a flat list of notifications, spread across three sections
+                listOf(
+                        notif(0, section0),
+                        notif(1, section0),
+                        notif(2, section1),
+                        notif(3, section2)
+                ),
+
+                // THEN each section has its header injected
+                tree(
+                        notifNode(0),
+                        notifNode(1),
+                        notifNode(2),
+                        notifNode(3)
+                )
+        )
+    }
+
+    @Test
     fun testGroups() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a mixed list of top-level notifications and groups
                 listOf(
@@ -218,6 +251,7 @@
 
     @Test
     fun testSecondSectionWithNoHeader() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a middle section with no associated header view
                 listOf(
@@ -247,6 +281,7 @@
 
     @Test(expected = RuntimeException::class)
     fun testRepeatedSectionsThrow() {
+        whenever(sectionHeaderVisibilityProvider.sectionHeadersVisible).thenReturn(true)
         checkOutput(
                 // GIVEN a malformed list where sections are not contiguous
                 listOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index a8522c7..6364d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -52,13 +52,14 @@
 @SmallTest
 public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase {
 
-    private static final String[] DEFAULT_SETTINGS = new String[] {"0:0", "1:2"};
+    private static final String[] DEFAULT_SETTINGS = new String[]{"0:1", "2:0:1", "1:2"};
 
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mFakeExecutor = new FakeExecutor(mFakeSystemClock);
-    @Mock DeviceStateManager mDeviceStateManager;
-    RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
-    DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
+    @Mock
+    private DeviceStateManager mDeviceStateManager;
+    private final RotationPolicyWrapper mFakeRotationPolicy = new FakeRotationPolicy();
+    private DeviceStateRotationLockSettingController mDeviceStateRotationLockSettingController;
     private DeviceStateManager.DeviceStateCallback mDeviceStateCallback;
     private DeviceStateRotationLockSettingsManager mSettingsManager;
     private TestableContentResolver mContentResolver;
@@ -93,7 +94,7 @@
                                 mContentResolver,
                                 Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                                 UserHandle.USER_CURRENT))
-                .isEqualTo("0:0:1:2");
+                .isEqualTo("0:1:1:2:2:0");
     }
 
     @Test
@@ -125,6 +126,31 @@
     }
 
     @Test
+    public void whenDeviceStateSwitched_settingIsIgnored_loadsDefaultFallbackSetting() {
+        initializeSettingsWith();
+        mFakeRotationPolicy.setRotationLock(true);
+
+        // State 2 -> Ignored -> Fall back to state 1 which is unlocked
+        mDeviceStateCallback.onStateChanged(2);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
+    }
+
+    @Test
+    public void whenDeviceStateSwitched_ignoredSetting_fallbackValueChanges_usesFallbackValue() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        mFakeRotationPolicy.setRotationLock(false);
+
+        // State 2 -> Ignored -> Fall back to state 1 which is locked
+        mDeviceStateCallback.onStateChanged(2);
+
+        assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
+    }
+
+    @Test
     public void whenUserChangesSetting_saveSettingForCurrentState() {
         initializeSettingsWith(
                 0, DEVICE_STATE_ROTATION_LOCK_LOCKED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
@@ -159,15 +185,15 @@
     }
 
     @Test
-    public void whenDeviceStateSwitchedToIgnoredState_newSettingsSaveForPreviousState() {
+    public void whenDeviceStateSwitchedToIgnoredState_noFallback_newSettingsSaveForPreviousState() {
         initializeSettingsWith(
-                0, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+                8, DEVICE_STATE_ROTATION_LOCK_IGNORED, 1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
         mFakeRotationPolicy.setRotationLock(true);
 
         mDeviceStateCallback.onStateChanged(1);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
-        mDeviceStateCallback.onStateChanged(0);
+        mDeviceStateCallback.onStateChanged(8);
         assertThat(mFakeRotationPolicy.isRotationLocked()).isFalse();
 
         mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
@@ -178,7 +204,7 @@
                                 mContentResolver,
                                 Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
                                 UserHandle.USER_CURRENT))
-                .isEqualTo("0:0:1:1");
+                .isEqualTo("1:1:8:0");
     }
 
     @Test
@@ -198,12 +224,78 @@
         assertThat(mFakeRotationPolicy.isRotationLocked()).isTrue();
     }
 
+    @Test
+    public void onRotationLockStateChanged_newSettingIsPersisted() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED);
+        mDeviceStateCallback.onStateChanged(0);
+
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ false,
+                /* affordanceVisible= */ true
+        );
+
+        assertThat(
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:2:1:2");
+    }
+
+    @Test
+    public void onRotationLockStateChanged_deviceStateIsIgnored_newSettingIsPersistedToFallback() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                2, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        mDeviceStateCallback.onStateChanged(2);
+
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ true,
+                /* affordanceVisible= */ true
+        );
+
+        assertThat(
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:1:1:1:2:0");
+    }
+
+    @Test
+    public void onRotationLockStateChange_stateIgnored_noFallback_settingIsPersistedToPrevious() {
+        initializeSettingsWith(
+                0, DEVICE_STATE_ROTATION_LOCK_LOCKED,
+                1, DEVICE_STATE_ROTATION_LOCK_UNLOCKED,
+                8, DEVICE_STATE_ROTATION_LOCK_IGNORED);
+        mDeviceStateCallback.onStateChanged(1);
+        mDeviceStateCallback.onStateChanged(8);
+
+        mDeviceStateRotationLockSettingController.onRotationLockStateChanged(
+                /* rotationLocked= */ true,
+                /* affordanceVisible= */ true
+        );
+
+        assertThat(
+                Settings.Secure.getStringForUser(
+                        mContentResolver,
+                        Settings.Secure.DEVICE_STATE_ROTATION_LOCK,
+                        UserHandle.USER_CURRENT))
+                .isEqualTo("0:1:1:1:8:0");
+    }
+
     private void initializeSettingsWith(int... values) {
         if (values.length % 2 != 0) {
             throw new IllegalArgumentException("Expecting key-value pairs");
         }
         StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < values.length; sb.append(":")) {
+        for (int i = 0; i < values.length; ) {
+            if (i > 0) {
+                sb.append(":");
+            }
             sb.append(values[i++]).append(":").append(values[i++]);
         }
 
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 93fc0e72..2b7b977 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -254,9 +254,14 @@
     private AssociationInfo createAssociationAndNotifyApplication(
             @NonNull AssociationRequest request, @NonNull String packageName, @UserIdInt int userId,
             @Nullable MacAddress macAddress, @NonNull IAssociationRequestCallback callback) {
-        final AssociationInfo association = mService.createAssociation(userId, packageName,
-                macAddress, request.getDisplayName(), request.getDeviceProfile(),
-                request.isSelfManaged());
+        final AssociationInfo association;
+        final long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            association = mService.createAssociation(userId, packageName, macAddress,
+                    request.getDisplayName(), request.getDeviceProfile(), request.isSelfManaged());
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
 
         try {
             callback.onAssociationCreated(association);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b2b5576..cfd3798 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -787,6 +787,12 @@
         Slog.i(LOG_TAG, "New CDM association created=" + association);
         mAssociationStore.addAssociation(association);
 
+        // If the "Device Profile" is specified, make the companion application a holder of the
+        // corresponding role.
+        if (deviceProfile != null) {
+            addRoleHolderForAssociation(getContext(), association);
+        }
+
         updateSpecialAccessPermissionForAssociatedPackage(association);
 
         return association;
@@ -930,14 +936,8 @@
 
         exemptFromAutoRevoke(packageInfo.packageName, packageInfo.applicationInfo.uid);
 
-        if (!association.isSelfManaged()) {
-            if (mCurrentlyConnectedDevices.contains(association.getDeviceMacAddressAsString())) {
-                addRoleHolderForAssociation(getContext(), association);
-            }
-
-            if (association.isNotifyOnDeviceNearby()) {
-                restartBleScan();
-            }
+        if (association.isNotifyOnDeviceNearby()) {
+            restartBleScan();
         }
     }
 
@@ -983,19 +983,7 @@
 
     void onDeviceConnected(String address) {
         Slog.d(LOG_TAG, "onDeviceConnected(address = " + address + ")");
-
         mCurrentlyConnectedDevices.add(address);
-
-        for (AssociationInfo association : mAssociationStore.getAssociationsByAddress(address)) {
-            if (association.getDeviceProfile() != null) {
-                Slog.i(LOG_TAG, "Granting role " + association.getDeviceProfile()
-                        + " to " + association.getPackageName()
-                        + " due to device connected: " + association.getDeviceMacAddress());
-
-                addRoleHolderForAssociation(getContext(), association);
-            }
-        }
-
         onDeviceNearby(address);
     }
 
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 9b2bd82..f2e66077 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -73,20 +73,12 @@
                 }
                 break;
 
-                case "simulate_connect": {
-                    mService.onDeviceConnected(getNextArgRequired());
-                }
-                break;
-
-                case "simulate_disconnect": {
-                    mService.onDeviceDisconnected(getNextArgRequired());
-                }
-                break;
                 case "clear-association-memory-cache": {
                     mService.persistState();
                     mService.loadAssociationsFromDisk();
                 }
                 break;
+
                 default:
                     return handleDefaultCommands(cmd);
             }
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
new file mode 100644
index 0000000..6055a81
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+
+import com.android.internal.util.FunctionalUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+
+final class DataStoreUtils {
+
+    private static final String LOG_TAG = DataStoreUtils.class.getSimpleName();
+
+    static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        return parser.getEventType() == START_TAG && tag.equals(parser.getName());
+    }
+
+    static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
+            throws XmlPullParserException {
+        return parser.getEventType() == END_TAG && tag.equals(parser.getName());
+    }
+
+    /**
+     * Creates {@link AtomicFile} object that represents the back-up for the given user.
+     *
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     *
+     * @param userId              the userId to retrieve the storage file
+     * @param fileName         the storage file name
+     * @return an AtomicFile for the user
+     */
+    @NonNull
+    static AtomicFile createStorageFileForUser(@UserIdInt int userId, String fileName) {
+        return new AtomicFile(getBaseStorageFileForUser(userId, fileName));
+    }
+
+    @NonNull
+    private static File getBaseStorageFileForUser(@UserIdInt int userId, String fileName) {
+        return new File(Environment.getDataSystemDeDirectory(userId), fileName);
+    }
+
+    /**
+     * Writing to file could fail, for example, if the user has been recently removed and so was
+     * their DE (/data/system_de/<user-id>/) directory.
+     */
+    static void writeToFileSafely(@NonNull AtomicFile file,
+            @NonNull FunctionalUtils.ThrowingConsumer<FileOutputStream> consumer) {
+        try {
+            file.write(consumer);
+        } catch (Exception e) {
+            Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+        }
+    }
+
+    private DataStoreUtils() {
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 3c8c3cb..da33b44 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -25,9 +25,10 @@
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
-
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -44,7 +45,6 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
-import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -53,7 +53,6 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.HashSet;
@@ -210,6 +209,7 @@
             @NonNull Collection<AssociationInfo> associationsOut,
             @NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
         Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
+
         final AtomicFile file = getStorageFileForUser(userId);
         if (DEBUG) Slog.d(LOG_TAG, "  > File=" + file.getBaseFile().getPath());
 
@@ -349,11 +349,7 @@
      */
     private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
         return mUserIdToStorageFile.computeIfAbsent(userId,
-                u -> new AtomicFile(getBaseStorageFileForUser(userId)));
-    }
-
-    private static @NonNull File getBaseStorageFileForUser(@UserIdInt int userId) {
-        return new File(Environment.getDataSystemDeDirectory(userId), FILE_NAME);
+                u -> createStorageFileForUser(userId, FILE_NAME));
     }
 
     private static @NonNull File getBaseLegacyStorageFileForUser(@UserIdInt int userId) {
@@ -512,16 +508,6 @@
         serializer.endTag(null, XML_TAG_PACKAGE);
     }
 
-    private static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
-            throws XmlPullParserException {
-        return parser.getEventType() == START_TAG && tag.equals(parser.getName());
-    }
-
-    private static boolean isEndOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
-            throws XmlPullParserException {
-        return parser.getEventType() == END_TAG && tag.equals(parser.getName());
-    }
-
     private static void requireStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
             throws XmlPullParserException {
         if (isStartOfTag(parser, tag)) return;
@@ -546,13 +532,4 @@
         }
         return associationInfo;
     }
-
-    private static void writeToFileSafely(@NonNull AtomicFile file,
-            @NonNull ThrowingConsumer<FileOutputStream> consumer) {
-        try {
-            file.write(consumer);
-        } catch (Exception e) {
-            Slog.e(LOG_TAG, "Error while writing to file " + file, e);
-        }
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
new file mode 100644
index 0000000..38e5d16
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/SystemDataTransferRequestDataStore.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion;
+
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readThisListXml;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeListXml;
+import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.companion.SystemDataTransferRequest;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * The class is responsible for reading/writing SystemDataTransferRequest records from/to the disk.
+ *
+ * The following snippet is a sample XML file stored in the disk.
+ * <pre>{@code
+ * <requests>
+ *   <request
+ *     association_id="1"
+ *     is_permission_sync_all_packages="false">
+ *     <list name="permission_sync_packages">
+ *       <string>com.sample.app1</string>
+ *       <string>com.sample.app2</string>
+ *     </list>
+ *   </request>
+ * </requests>
+ * }</pre>
+ */
+public class SystemDataTransferRequestDataStore {
+
+    private static final String LOG_TAG = SystemDataTransferRequestDataStore.class.getSimpleName();
+
+    private static final String FILE_NAME = "companion_device_system_data_transfer_requests.xml";
+
+    private static final String XML_TAG_REQUESTS = "requests";
+    private static final String XML_TAG_REQUEST = "request";
+    private static final String XML_TAG_LIST = "list";
+
+    private static final String XML_ATTR_ASSOCIATION_ID = "association_id";
+    private static final String XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES =
+            "is_permission_sync_all_packages";
+    private static final String XML_ATTR_PERMISSION_SYNC_PACKAGES = "permission_sync_packages";
+
+    private final ConcurrentMap<Integer, AtomicFile> mUserIdToStorageFile =
+            new ConcurrentHashMap<>();
+
+    /**
+     * Reads previously persisted data for the given user
+     *
+     * @param userId Android UserID
+     * @return a list of SystemDataTransferRequest
+     */
+    @NonNull
+    List<SystemDataTransferRequest> readRequestsForUser(@UserIdInt int userId) {
+        final AtomicFile file = getStorageFileForUser(userId);
+        Slog.i(LOG_TAG, "Reading SystemDataTransferRequests for user " + userId + " from "
+                + "file=" + file.getBaseFile().getPath());
+
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
+        synchronized (file) {
+            if (!file.getBaseFile().exists()) {
+                Slog.d(LOG_TAG, "File does not exist -> Abort");
+                return Collections.emptyList();
+            }
+            try (FileInputStream in = file.openRead()) {
+                final TypedXmlPullParser parser = Xml.resolvePullParser(in);
+                XmlUtils.beginDocument(parser, XML_TAG_REQUESTS);
+
+                return readRequests(parser);
+            } catch (XmlPullParserException | IOException e) {
+                Slog.e(LOG_TAG, "Error while reading requests file", e);
+                return Collections.emptyList();
+            }
+        }
+    }
+
+    @NonNull
+    private List<SystemDataTransferRequest> readRequests(@NonNull TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        if (!isStartOfTag(parser, XML_TAG_REQUESTS)) {
+            throw new XmlPullParserException("The XML doesn't have start tag: " + XML_TAG_REQUESTS);
+        }
+
+        List<SystemDataTransferRequest> requests = new ArrayList<>();
+
+        while (true) {
+            parser.nextTag();
+            if (isEndOfTag(parser, XML_TAG_REQUESTS)) break;
+            if (isStartOfTag(parser, XML_TAG_REQUEST)) {
+                requests.add(readRequest(parser));
+            }
+        }
+
+        return requests;
+    }
+
+    private SystemDataTransferRequest readRequest(@NonNull TypedXmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        if (!isStartOfTag(parser, XML_TAG_REQUEST)) {
+            throw new XmlPullParserException("XML doesn't have start tag: " + XML_TAG_REQUEST);
+        }
+
+        final int associationId = readIntAttribute(parser, XML_ATTR_ASSOCIATION_ID);
+        final boolean isPermissionSyncAllPackages = readBooleanAttribute(parser,
+                XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES);
+        parser.nextTag();
+        List<String> permissionSyncPackages = new ArrayList<>();
+        if (isStartOfTag(parser, XML_TAG_LIST)) {
+            parser.nextTag();
+            permissionSyncPackages = readThisListXml(parser, XML_TAG_LIST,
+                    new String[1]);
+        }
+
+        return new SystemDataTransferRequest(associationId, isPermissionSyncAllPackages,
+                permissionSyncPackages);
+    }
+
+    /**
+     * Persisted user's SystemDataTransferRequest data to the disk.
+     *
+     * @param userId   Android UserID
+     * @param requests a list of user's SystemDataTransferRequest.
+     */
+    void writeRequestsForUser(@UserIdInt int userId,
+            @NonNull List<SystemDataTransferRequest> requests) {
+        final AtomicFile file = getStorageFileForUser(userId);
+        Slog.i(LOG_TAG, "Writing SystemDataTransferRequests for user " + userId + " to file="
+                + file.getBaseFile().getPath());
+
+        // getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
+        // accesses to the file on the file system using this AtomicFile object.
+        synchronized (file) {
+            writeToFileSafely(file, out -> {
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(out);
+                serializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+                serializer.startDocument(null, true);
+                writeRequests(serializer, requests);
+                serializer.endDocument();
+            });
+        }
+    }
+
+    private void writeRequests(@NonNull TypedXmlSerializer serializer,
+            @Nullable Collection<SystemDataTransferRequest> requests) throws IOException {
+        serializer.startTag(null, XML_TAG_REQUESTS);
+
+        for (SystemDataTransferRequest request : requests) {
+            writeRequest(serializer, request);
+        }
+
+        serializer.endTag(null, XML_TAG_REQUESTS);
+    }
+
+    private void writeRequest(@NonNull TypedXmlSerializer serializer,
+            @NonNull SystemDataTransferRequest request) throws IOException {
+        serializer.startTag(null, XML_TAG_REQUEST);
+
+        writeIntAttribute(serializer, XML_ATTR_ASSOCIATION_ID, request.getAssociationId());
+        writeBooleanAttribute(serializer, XML_ATTR_IS_PERMISSION_SYNC_ALL_PACKAGES,
+                request.isPermissionSyncAllPackages());
+        try {
+            writeListXml(request.getPermissionSyncPackages(), XML_ATTR_PERMISSION_SYNC_PACKAGES,
+                    serializer);
+        } catch (XmlPullParserException e) {
+            Slog.e(LOG_TAG, "Error writing permission sync packages into XML. "
+                    + request.getPermissionSyncPackages().toString());
+        }
+
+        serializer.endTag(null, XML_TAG_REQUEST);
+    }
+
+    /**
+     * Creates and caches {@link AtomicFile} object that represents the back-up file for the given
+     * user.
+     *
+     * IMPORTANT: the method will ALWAYS return the same {@link AtomicFile} object, which makes it
+     * possible to synchronize reads and writes to the file using the returned object.
+     */
+    private @NonNull AtomicFile getStorageFileForUser(@UserIdInt int userId) {
+        return mUserIdToStorageFile.computeIfAbsent(userId,
+                u -> createStorageFileForUser(userId, FILE_NAME));
+    }
+}
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
new file mode 100644
index 0000000..0eb6b8d
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.companion.presence;
+
+import static android.bluetooth.BluetoothAdapter.ACTION_BLE_STATE_CHANGED;
+import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
+import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
+import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
+import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
+import static android.bluetooth.BluetoothAdapter.STATE_ON;
+import static android.bluetooth.BluetoothAdapter.nameForState;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES;
+import static android.bluetooth.le.ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_ALL_MATCHES;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_FIRST_MATCH;
+import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST;
+import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
+
+import static com.android.server.companion.presence.Utils.btDeviceToString;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.companion.AssociationInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.server.companion.AssociationStore;
+import com.android.server.companion.AssociationStore.ChangeType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@SuppressLint("LongLogTag")
+class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
+
+    /**
+     * When using {@link ScanSettings#SCAN_MODE_LOW_POWER}, it usually takes from 20 seconds to
+     * 2 minutes for the BLE scanner to find advertisements sent from the same device.
+     * On the other hand, {@link android.bluetooth.BluetoothAdapter.LeScanCallback} will report
+     * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST MATCH_LOST} 10 sec after it finds the
+     * advertisement for the first time (add reports
+     * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH FIRST_MATCH}).
+     * To avoid constantly reporting {@link Callback#onBleCompanionDeviceFound(int) onDeviceFound()}
+     * and {@link Callback#onBleCompanionDeviceLost(int) onDeviceLost()} (while the device is
+     * actually present) to its clients, {@link BleCompanionDeviceScanner}, will add 1-minute delay
+     * after it receives {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST MATCH_LOST}.
+     */
+    private static final int NOTIFY_DEVICE_LOST_DELAY = 2 * 60 * 1000; // 2 Min.
+
+    interface Callback {
+        void onBleCompanionDeviceFound(int associationId);
+
+        void onBleCompanionDeviceLost(int associationId);
+    }
+
+    private final @NonNull AssociationStore mAssociationStore;
+    private final @NonNull Callback mCallback;
+    private final @NonNull MainThreadHandler mMainThreadHandler;
+
+    // Non-null after init().
+    private @Nullable BluetoothAdapter mBtAdapter;
+    // Non-null after init() and when BLE is available. Otherwise - null.
+    private @Nullable BluetoothLeScanner mBleScanner;
+    // Only accessed from the Main thread.
+    private boolean mScanning = false;
+
+    BleCompanionDeviceScanner(
+            @NonNull AssociationStore associationStore, @NonNull Callback callback) {
+        mAssociationStore = associationStore;
+        mCallback = callback;
+        mMainThreadHandler = new MainThreadHandler();
+    }
+
+    @MainThread
+    void init(@NonNull Context context, @NonNull BluetoothAdapter btAdapter) {
+        if (DEBUG) Log.i(TAG, "init()");
+
+        if (mBtAdapter != null) {
+            throw new IllegalStateException(getClass().getSimpleName() + " is already initialized");
+        }
+        mBtAdapter = requireNonNull(btAdapter);
+
+        checkBleState();
+        registerBluetoothStateBroadcastReceiver(context);
+
+        mAssociationStore.registerListener(this);
+    }
+
+    @MainThread
+    final void restartScan() {
+        enforceInitialized();
+
+        if (DEBUG) Log.i(TAG , "restartScan()");
+        if (mBleScanner == null) {
+            if (DEBUG) Log.d(TAG, "  > BLE is not available");
+            return;
+        }
+
+        stopScanIfNeeded();
+        startScan();
+    }
+
+    @Override
+    public void onAssociationChanged(@ChangeType int changeType, AssociationInfo association) {
+        // Simply restart scanning.
+        if (Looper.getMainLooper().isCurrentThread()) {
+            restartScan();
+        } else {
+            mMainThreadHandler.post(this::restartScan);
+        }
+    }
+
+    @MainThread
+    private void checkBleState() {
+        enforceInitialized();
+
+        final boolean bleAvailable = isBleAvailable();
+        if (DEBUG) {
+            Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable);
+        }
+        if ((bleAvailable && mBleScanner != null) || (!bleAvailable && mBleScanner == null)) {
+            // Nothing changed.
+            if (DEBUG) Log.i(TAG, "  > BLE status did not change");
+            return;
+        }
+
+        if (bleAvailable) {
+            mBleScanner = mBtAdapter.getBluetoothLeScanner();
+            if (mBleScanner == null) {
+                // Oops, that's a race condition. Can return.
+                return;
+            }
+            if (DEBUG) Log.i(TAG, "  > BLE is now available");
+
+            startScan();
+        } else {
+            if (DEBUG) Log.i(TAG, "  > BLE is now unavailable");
+
+            stopScanIfNeeded();
+            mBleScanner = null;
+        }
+    }
+
+    /**
+     * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private
+     * access level, so it's not accessible from here.
+     */
+    private boolean isBleAvailable() {
+        final int state = mBtAdapter.getLeState();
+        if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state));
+        return state == STATE_ON || state == STATE_BLE_ON;
+    }
+
+    @MainThread
+    private void startScan() {
+        enforceInitialized();
+        // This method should not be called if scan is already in progress.
+        if (mScanning) throw new IllegalStateException("Scan is already in progress.");
+        // Neither should this method be called if the adapter is not available.
+        if (mBleScanner == null) throw new IllegalStateException("BLE is not available.");
+
+        if (DEBUG) Log.i(TAG, "startScan()");
+
+        // Collect MAC addresses from all associations.
+        final Set<String> macAddresses = new HashSet<>();
+        for (AssociationInfo association : mAssociationStore.getAssociations()) {
+            // Beware that BT stack does not consider low-case MAC addresses valid, while
+            // MacAddress.toString() return a low-case String.
+            final String macAddress = association.getDeviceMacAddressAsString();
+            if (macAddress != null) {
+                macAddresses.add(macAddress);
+            }
+        }
+        if (macAddresses.isEmpty()) {
+            if (DEBUG) Log.i(TAG, "  > there are no (associated) devices to Scan for.");
+            return;
+        } else {
+            if (DEBUG) {
+                Log.d(TAG, "  > addresses=(n=" + macAddresses.size() + ")"
+                        + "[" + String.join(", ", macAddresses) + "]");
+            }
+        }
+
+        final List<ScanFilter> filters = new ArrayList<>(macAddresses.size());
+        for (String macAddress : macAddresses) {
+            final ScanFilter filter = new ScanFilter.Builder()
+                    .setDeviceAddress(macAddress)
+                    .build();
+            filters.add(filter);
+        }
+
+        mBleScanner.startScan(filters, SCAN_SETTINGS, mScanCallback);
+        mScanning = true;
+    }
+
+    private void stopScanIfNeeded() {
+        enforceInitialized();
+
+        if (DEBUG) Log.i(TAG, "stopScan()");
+        if (!mScanning) {
+            Log.d(TAG, "  > not scanning.");
+            return;
+        }
+
+        mBleScanner.stopScan(mScanCallback);
+        mScanning = false;
+    }
+
+    @MainThread
+    private void notifyDeviceFound(@NonNull BluetoothDevice device) {
+        if (DEBUG) Log.i(TAG, "notifyDevice_Found()" + btDeviceToString(device));
+
+        final List<AssociationInfo> associations =
+                mAssociationStore.getAssociationsByAddress(device.getAddress());
+        if (DEBUG) Log.d(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+
+        for (AssociationInfo association : associations) {
+            mCallback.onBleCompanionDeviceFound(association.getId());
+        }
+    }
+
+    @MainThread
+    private void notifyDeviceLost(@NonNull BluetoothDevice device) {
+        if (DEBUG) Log.i(TAG, "notifyDevice_Lost()" + btDeviceToString(device));
+
+        final List<AssociationInfo> associations =
+                mAssociationStore.getAssociationsByAddress(device.getAddress());
+        if (DEBUG) Log.d(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+
+        for (AssociationInfo association : associations) {
+            mCallback.onBleCompanionDeviceLost(association.getId());
+        }
+    }
+
+    private void registerBluetoothStateBroadcastReceiver(Context context) {
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int prevState = intent.getIntExtra(EXTRA_PREVIOUS_STATE, -1);
+                final int state = intent.getIntExtra(EXTRA_STATE, -1);
+
+                if (DEBUG) {
+                    // The action is either STATE_CHANGED or BLE_STATE_CHANGED.
+                    final String action =
+                            intent.getAction().replace("android.bluetooth.adapter.", "bt.");
+                    Log.d(TAG, "on(Broadcast)Receive() " + action + ": "
+                            + nameForBtState(prevState) + "->" + nameForBtState(state));
+                }
+
+                checkBleState();
+            }
+        };
+
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_STATE_CHANGED);
+        filter.addAction(ACTION_BLE_STATE_CHANGED);
+
+        context.registerReceiver(receiver, filter);
+    }
+
+    private void enforceInitialized() {
+        if (mBtAdapter != null) return;
+        throw new IllegalStateException(getClass().getSimpleName() + " is not initialized");
+    }
+
+    private final ScanCallback mScanCallback = new ScanCallback() {
+        @MainThread
+        @Override
+        public void onScanResult(int callbackType, ScanResult result) {
+            final BluetoothDevice device = result.getDevice();
+
+            if (DEBUG) {
+                Log.d(TAG, "onScanResult() " + nameForBleScanCallbackType(callbackType)
+                        + " device=" + btDeviceToString(device));
+                Log.v(TAG, "  > scanResult=" + result);
+
+                final List<AssociationInfo> associations =
+                        mAssociationStore.getAssociationsByAddress(device.getAddress());
+                Log.v(TAG, "  > associations=" + Arrays.toString(associations.toArray()));
+            }
+
+            switch (callbackType) {
+                case CALLBACK_TYPE_FIRST_MATCH:
+                    if (mMainThreadHandler.hasNotifyDeviceLostMessages(device)) {
+                        mMainThreadHandler.removeNotifyDeviceLostMessages(device);
+                        return;
+                    }
+
+                    notifyDeviceFound(device);
+                    break;
+
+                case CALLBACK_TYPE_MATCH_LOST:
+                    mMainThreadHandler.sendNotifyDeviceLostDelayedMessage(device);
+                    break;
+
+                default:
+                    Slog.wtf(TAG, "Unexpected callback "
+                            + nameForBleScanCallbackType(callbackType));
+                    break;
+            }
+        }
+
+        @MainThread
+        @Override
+        public void onScanFailed(int errorCode) {
+            if (DEBUG) Log.w(TAG, "onScanFailed() " + nameForBleScanErrorCode(errorCode));
+            mScanning = false;
+        }
+    };
+
+    @SuppressLint("HandlerLeak")
+    private class MainThreadHandler extends Handler {
+        private static final int NOTIFY_DEVICE_LOST = 1;
+
+        MainThreadHandler() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(@NonNull Message message) {
+            if  (message.what != NOTIFY_DEVICE_LOST) return;
+
+            final BluetoothDevice device = (BluetoothDevice) message.obj;
+            notifyDeviceLost(device);
+        }
+
+        void sendNotifyDeviceLostDelayedMessage(BluetoothDevice device) {
+            final Message message = obtainMessage(NOTIFY_DEVICE_LOST, device);
+            sendMessageDelayed(message, NOTIFY_DEVICE_LOST_DELAY);
+        }
+
+        boolean hasNotifyDeviceLostMessages(BluetoothDevice device) {
+            return hasEqualMessages(NOTIFY_DEVICE_LOST, device);
+        }
+
+        void removeNotifyDeviceLostMessages(BluetoothDevice device) {
+            removeEqualMessages(NOTIFY_DEVICE_LOST, device);
+        }
+    }
+
+    private static String nameForBtState(int state) {
+        return nameForState(state) + "(" + state + ")";
+    }
+
+    private static String nameForBleScanCallbackType(int callbackType) {
+        final String name;
+        switch (callbackType) {
+            case CALLBACK_TYPE_ALL_MATCHES:
+                name = "ALL_MATCHES";
+                break;
+            case CALLBACK_TYPE_FIRST_MATCH:
+                name = "FIRST_MATCH";
+                break;
+            case CALLBACK_TYPE_MATCH_LOST:
+                name = "MATCH_LOST";
+                break;
+            default:
+                name = "Unknown";
+        }
+        return name + "(" + callbackType + ")";
+    }
+
+    private static String nameForBleScanErrorCode(int errorCode) {
+        final String name;
+        switch (errorCode) {
+            case SCAN_FAILED_ALREADY_STARTED:
+                name = "ALREADY_STARTED";
+                break;
+            case SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
+                name = "APPLICATION_REGISTRATION_FAILED";
+                break;
+            case SCAN_FAILED_INTERNAL_ERROR:
+                name = "INTERNAL_ERROR";
+                break;
+            case SCAN_FAILED_FEATURE_UNSUPPORTED:
+                name = "FEATURE_UNSUPPORTED";
+                break;
+            case SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES:
+                name = "OUT_OF_HARDWARE_RESOURCES";
+                break;
+            case SCAN_FAILED_SCANNING_TOO_FREQUENTLY:
+                name = "SCANNING_TOO_FREQUENTLY";
+                break;
+            default:
+                name = "Unknown";
+        }
+        return name + "(" + errorCode + ")";
+    }
+
+    private static final ScanSettings SCAN_SETTINGS = new ScanSettings.Builder()
+            .setCallbackType(CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST)
+            .setScanMode(SCAN_MODE_LOW_POWER)
+            .build();
+}
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index a4fa1c1..dbe866b 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -16,6 +16,8 @@
 
 package com.android.server.companion.presence;
 
+import static com.android.server.companion.presence.Utils.btDeviceToString;
+
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.bluetooth.BluetoothAdapter;
@@ -71,11 +73,11 @@
      */
     @Override
     public void onDeviceConnected(@NonNull BluetoothDevice device) {
-        if (DEBUG) Log.i(TAG, "onDevice_Connected() " + toString(device));
+        if (DEBUG) Log.i(TAG, "onDevice_Connected() " + btDeviceToString(device));
 
         final MacAddress macAddress = MacAddress.fromString(device.getAddress());
         if (mAllConnectedDevices.put(macAddress, device) != null) {
-            if (DEBUG) Log.w(TAG, "Device " + toString(device) + " is already connected.");
+            if (DEBUG) Log.w(TAG, "Device " + btDeviceToString(device) + " is already connected.");
             return;
         }
 
@@ -91,13 +93,15 @@
     public void onDeviceDisconnected(@NonNull BluetoothDevice device,
             @DisconnectReason int reason) {
         if (DEBUG) {
-            Log.i(TAG, "onDevice_Disconnected() " + toString(device));
+            Log.i(TAG, "onDevice_Disconnected() " + btDeviceToString(device));
             Log.d(TAG, "  reason=" + disconnectReasonText(reason));
         }
 
         final MacAddress macAddress = MacAddress.fromString(device.getAddress());
         if (mAllConnectedDevices.remove(macAddress) == null) {
-            if (DEBUG) Log.w(TAG, "The device wasn't tracked as connected " + toString(device));
+            if (DEBUG) {
+                Log.w(TAG, "The device wasn't tracked as connected " + btDeviceToString(device));
+            }
             return;
         }
 
@@ -109,7 +113,7 @@
                 mAssociationStore.getAssociationsByAddress(device.getAddress());
 
         if (DEBUG) {
-            Log.d(TAG, "onDevice_ConnectivityChanged() " + toString(device)
+            Log.d(TAG, "onDevice_ConnectivityChanged() " + btDeviceToString(device)
                     + " connected=" + connected);
             if (associations.isEmpty()) {
                 Log.d(TAG, "  > No CDM associations");
@@ -138,6 +142,12 @@
     }
 
     @Override
+    public void onAssociationRemoved(AssociationInfo association) {
+        // Intentionally do nothing: CompanionDevicePresenceMonitor will do all the bookkeeping
+        // required.
+    }
+
+    @Override
     public void onAssociationUpdated(AssociationInfo association, boolean addressChanged) {
         if (DEBUG) {
             Log.d(TAG, "onAssociation_Updated() addrChange=" + addressChanged
@@ -153,23 +163,4 @@
         // This will be implemented when CDM support updating addresses.
         throw new IllegalArgumentException("Address changes are not supported.");
     }
-
-    private static String toString(@NonNull BluetoothDevice btDevice) {
-        final StringBuilder sb = new StringBuilder(btDevice.getAddress());
-
-        sb.append(" [name=");
-        final String name = btDevice.getName();
-        if (name != null) {
-            sb.append('\'').append(name).append('\'');
-        } else {
-            sb.append("null");
-        }
-
-        final String alias = btDevice.getAlias();
-        if (alias != null) {
-            sb.append(", alias='").append(alias).append("'");
-        }
-
-        return sb.append(']').toString();
-    }
 }
diff --git a/services/companion/java/com/android/server/companion/presence/Utils.java b/services/companion/java/com/android/server/companion/presence/Utils.java
new file mode 100644
index 0000000..583b443
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/Utils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.presence;
+
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
+
+/** Utilities for working with Bluetooth and BLE devices. */
+class Utils {
+
+    /**
+     * @return short String representation of {@link BluetoothDevice}.
+     */
+    static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
+        final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+        sb.append(" [name=");
+        final String name = btDevice.getName();
+        if (name != null) {
+            sb.append('\'').append(name).append('\'');
+        } else {
+            sb.append("null");
+        }
+
+        final String alias = btDevice.getAlias();
+        if (alias != null) {
+            sb.append(", alias='").append(alias).append("'");
+        }
+
+        return sb.append(']').toString();
+    }
+
+    private Utils() {
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e040319..8887108 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4138,7 +4138,7 @@
             // for a previous process to come up.  To deal with this, we store
             // in the service any current isolated process it is running in or
             // waiting to have come up.
-            app = r.isolatedProc;
+            app = r.isolationHostProc;
             if (WebViewZygote.isMultiprocessEnabled()
                     && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                 hostingRecord = HostingRecord.byWebviewZygote(r.instanceName);
@@ -4165,7 +4165,7 @@
                 return msg;
             }
             if (isolated) {
-                r.isolatedProc = app;
+                r.isolationHostProc = app;
             }
         }
 
@@ -4976,7 +4976,7 @@
             try {
                 for (int i=0; i<mPendingServices.size(); i++) {
                     sr = mPendingServices.get(i);
-                    if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+                    if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
                             || !processName.equals(sr.processName))) {
                         continue;
                     }
@@ -5016,7 +5016,7 @@
             boolean didImmediateRestart = false;
             for (int i=0; i<mRestartingServices.size(); i++) {
                 sr = mRestartingServices.get(i);
-                if (proc != sr.isolatedProc && (proc.uid != sr.appInfo.uid
+                if (proc != sr.isolationHostProc && (proc.uid != sr.appInfo.uid
                         || !processName.equals(sr.processName))) {
                     continue;
                 }
@@ -5048,9 +5048,9 @@
             ServiceRecord sr = mPendingServices.get(i);
             if ((proc.uid == sr.appInfo.uid
                     && proc.processName.equals(sr.processName))
-                    || sr.isolatedProc == proc) {
+                    || sr.isolationHostProc == proc) {
                 Slog.w(TAG, "Forcing bringing down service: " + sr);
-                sr.isolatedProc = null;
+                sr.isolationHostProc = null;
                 mPendingServices.remove(i);
                 size = mPendingServices.size();
                 i--;
@@ -5083,7 +5083,7 @@
                     stopServiceAndUpdateAllowlistManagerLocked(service);
                 }
                 service.setProcess(null, null, 0, null);
-                service.isolatedProc = null;
+                service.isolationHostProc = null;
                 if (mTmpCollectionResults == null) {
                     mTmpCollectionResults = new ArrayList<>();
                 }
@@ -5321,7 +5321,7 @@
                 sr.app.mServices.updateBoundClientUids();
             }
             sr.setProcess(null, null, 0, null);
-            sr.isolatedProc = null;
+            sr.isolationHostProc = null;
             sr.executeNesting = 0;
             synchronized (mAm.mProcessStats.mLock) {
                 sr.forceClearTracker();
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index b123496..bdfd02e 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -598,7 +598,7 @@
             for (int i = psr.numberOfConnections() - 1; i >= 0; i--) {
                 ConnectionRecord cr = psr.getConnectionAt(i);
                 ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
-                        ? cr.binding.service.isolatedProc : cr.binding.service.app;
+                        ? cr.binding.service.isolationHostProc : cr.binding.service.app;
                 if (service == null || service == pr) {
                     continue;
                 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index c830554..be187e2 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -513,7 +513,7 @@
             }
         }
         processInfo = procInfo;
-        isolated = _info.uid != _uid;
+        isolated = Process.isIsolated(_uid);
         appZygote = (UserHandle.getAppId(_uid) >= Process.FIRST_APP_ZYGOTE_ISOLATED_UID
                 && UserHandle.getAppId(_uid) <= Process.LAST_APP_ZYGOTE_ISOLATED_UID);
         uid = _uid;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 9b32e61..24e7ba4 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -102,7 +102,8 @@
                             // IBinder -> ConnectionRecord of all bound clients
 
     ProcessRecord app;      // where this service is running or null.
-    ProcessRecord isolatedProc; // keep track of isolated process, if requested
+    ProcessRecord isolationHostProc; // process which we've started for this service (used for
+                                     // isolated and supplemental processes)
     ServiceState tracker; // tracking service execution, may be null
     ServiceState restartTracker; // tracking service restart
     boolean allowlistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
@@ -352,8 +353,8 @@
         if (app != null) {
             app.dumpDebug(proto, ServiceRecordProto.APP);
         }
-        if (isolatedProc != null) {
-            isolatedProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
+        if (isolationHostProc != null) {
+            isolationHostProc.dumpDebug(proto, ServiceRecordProto.ISOLATED_PROC);
         }
         proto.write(ServiceRecordProto.WHITELIST_MANAGER, allowlistManager);
         proto.write(ServiceRecordProto.DELAYED, delayed);
@@ -455,8 +456,8 @@
             pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
         }
         pw.print(prefix); pw.print("app="); pw.println(app);
-        if (isolatedProc != null) {
-            pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
+        if (isolationHostProc != null) {
+            pw.print(prefix); pw.print("isolationHostProc="); pw.println(isolationHostProc);
         }
         if (allowlistManager) {
             pw.print(prefix); pw.print("allowlistManager="); pw.println(allowlistManager);
diff --git a/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java b/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
index c985d5d..f7b73688 100644
--- a/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
+++ b/services/core/java/com/android/server/biometrics/log/CallbackWithProbe.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 
 /**
  * Client monitor callback that exposes a probe.
@@ -27,7 +28,7 @@
  *
  * @param <T> probe type
  */
-public class CallbackWithProbe<T extends Probe> implements BaseClientMonitor.Callback {
+public class CallbackWithProbe<T extends Probe> implements ClientMonitorCallback {
     private final boolean mStartWithClient;
     private final T mProbe;
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index e29caa8..86d72ba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -138,7 +138,7 @@
     }
 
     @Override
-    public void cancelWithoutStarting(@NonNull Callback callback) {
+    public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
         Slog.d(TAG, "cancelWithoutStarting: " + this);
 
         final int errorCode = BiometricConstants.BIOMETRIC_ERROR_CANCELED;
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index 0eb5aaf..35a0f57 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -91,7 +91,7 @@
 
     /**
      * Handles lifecycle, e.g. {@link BiometricScheduler},
-     * {@link com.android.server.biometrics.sensors.BaseClientMonitor.Callback} after authentication
+     * {@link ClientMonitorCallback} after authentication
      * results are known. Note that this happens asynchronously from (but shortly after)
      * {@link #onAuthenticated(BiometricAuthenticator.Identifier, boolean, ArrayList)} and allows
      * {@link CoexCoordinator} a chance to invoke/delay this event.
@@ -440,7 +440,7 @@
      * Start authentication
      */
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         final @LockoutTracker.LockoutMode int lockoutMode =
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 1248c8b..e1f7e2a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java
@@ -29,8 +29,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.biometrics.log.BiometricLogger;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.NoSuchElementException;
 
 /**
@@ -46,63 +44,6 @@
     // Counter used to distinguish between ClientMonitor instances to help debugging.
     private static int sCount = 0;
 
-    /**
-     * Interface that ClientMonitor holders should use to receive callbacks.
-     */
-    public interface Callback {
-        /**
-         * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
-         * the queue and becomes the current operation).
-         *
-         * @param clientMonitor Reference of the ClientMonitor that is starting.
-         */
-        default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
-        }
-
-        /**
-         * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
-         * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
-         * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
-         * implementation.
-         *
-         * @param clientMonitor Reference of the ClientMonitor that finished.
-         * @param success True if the operation completed successfully.
-         */
-        default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
-        }
-    }
-
-    /** Holder for wrapping multiple handlers into a single Callback. */
-    public static class CompositeCallback implements Callback {
-        @NonNull
-        private final List<Callback> mCallbacks;
-
-        public CompositeCallback(@NonNull Callback... 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.size(); i++) {
-                mCallbacks.get(i).onClientStarted(clientMonitor);
-            }
-        }
-
-        @Override
-        public final void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
-                boolean success) {
-            for (int i = mCallbacks.size() - 1; i >= 0; i--) {
-                mCallbacks.get(i).onClientFinished(clientMonitor, success);
-            }
-        }
-    }
-
     private final int mSequentialId;
     @NonNull private final Context mContext;
     private final int mTargetUserId;
@@ -120,7 +61,7 @@
 
     // Use an empty callback by default since delayed operations can receive events
     // before they are started and cause NPE in subclasses that access this field directly.
-    @NonNull protected Callback mCallback = new Callback() {
+    @NonNull protected ClientMonitorCallback mCallback = new ClientMonitorCallback() {
         @Override
         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
             Slog.e(TAG, "mCallback onClientStarted: called before set (should not happen)");
@@ -134,18 +75,6 @@
     };
 
     /**
-     * @return A ClientMonitorEnum constant defined in biometrics.proto
-     */
-    public abstract int getProtoEnum();
-
-    /**
-     * @return True if the ClientMonitor should cancel any current and pending interruptable clients
-     */
-    public boolean interruptsPrecedingClients() {
-        return false;
-    }
-
-    /**
      * @param context    system_server context
      * @param token      a unique token for the client
      * @param listener   recipient of related events (e.g. authentication)
@@ -189,11 +118,19 @@
         }
     }
 
+    /** A ClientMonitorEnum constant defined in biometrics.proto */
+    public abstract int getProtoEnum();
+
+    /** True if the ClientMonitor should cancel any current and pending interruptable clients. */
+    public boolean interruptsPrecedingClients() {
+        return false;
+    }
+
     /**
      * Starts the ClientMonitor's lifecycle.
      * @param callback invoked when the operation is complete (succeeds, fails, etc)
      */
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         mCallback = wrapCallbackForStart(callback);
         mCallback.onClientStarted(this);
     }
@@ -204,7 +141,7 @@
      * Returns the original callback unless overridden.
      */
     @NonNull
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
         return callback;
     }
 
@@ -329,7 +266,7 @@
     }
 
     @VisibleForTesting
-    public Callback getCallback() {
+    public ClientMonitorCallback getCallback() {
         return mCallback;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index 39c5944..1a6da94 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -160,7 +160,7 @@
     // Internal callback, notified when an operation is complete. Notifies the requester
     // that the operation is complete, before performing internal scheduler work (such as
     // starting the next client).
-    private final BaseClientMonitor.Callback mInternalCallback = new BaseClientMonitor.Callback() {
+    private final ClientMonitorCallback mInternalCallback = new ClientMonitorCallback() {
         @Override
         public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
             Slog.d(getTag(), "[Started] " + clientMonitor);
@@ -247,7 +247,7 @@
     }
 
     @VisibleForTesting
-    public BaseClientMonitor.Callback getInternalCallback() {
+    public ClientMonitorCallback getInternalCallback() {
         return mInternalCallback;
     }
 
@@ -368,7 +368,7 @@
      * @param clientCallback optional callback, invoked when the client state changes.
      */
     public void scheduleClientMonitor(@NonNull BaseClientMonitor clientMonitor,
-            @Nullable BaseClientMonitor.Callback clientCallback) {
+            @Nullable ClientMonitorCallback clientCallback) {
         // If the incoming operation should interrupt preceding clients, mark any interruptable
         // pending clients as canceling. Once they reach the head of the queue, the scheduler will
         // send ERROR_CANCELED and skip the operation.
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index e8b50d9..812ca8a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -65,7 +65,7 @@
     protected static final int STATE_WAITING_FOR_COOKIE = 4;
 
     /**
-     * The {@link BaseClientMonitor.Callback} has been invoked and the client is finished.
+     * The {@link ClientMonitorCallback} has been invoked and the client is finished.
      */
     protected static final int STATE_FINISHED = 5;
 
@@ -83,7 +83,7 @@
     @NonNull
     private final BaseClientMonitor mClientMonitor;
     @Nullable
-    private final BaseClientMonitor.Callback mClientCallback;
+    private final ClientMonitorCallback mClientCallback;
     @OperationState
     private int mState;
     @VisibleForTesting
@@ -92,14 +92,14 @@
 
     BiometricSchedulerOperation(
             @NonNull BaseClientMonitor clientMonitor,
-            @Nullable BaseClientMonitor.Callback callback
+            @Nullable ClientMonitorCallback callback
     ) {
         this(clientMonitor, callback, STATE_WAITING_IN_QUEUE);
     }
 
     protected BiometricSchedulerOperation(
             @NonNull BaseClientMonitor clientMonitor,
-            @Nullable BaseClientMonitor.Callback callback,
+            @Nullable ClientMonitorCallback callback,
             @OperationState int state
     ) {
         mClientMonitor = clientMonitor;
@@ -139,7 +139,7 @@
      * @param callback lifecycle callback
      * @return if this operation started
      */
-    public boolean start(@NonNull BaseClientMonitor.Callback callback) {
+    public boolean start(@NonNull ClientMonitorCallback callback) {
         checkInState("start",
                 STATE_WAITING_IN_QUEUE,
                 STATE_WAITING_FOR_COOKIE,
@@ -159,7 +159,7 @@
      * @param cookie   cookie indicting the operation should begin
      * @return if this operation started
      */
-    public boolean startWithCookie(@NonNull BaseClientMonitor.Callback callback, int cookie) {
+    public boolean startWithCookie(@NonNull ClientMonitorCallback callback, int cookie) {
         checkInState("start",
                 STATE_WAITING_IN_QUEUE,
                 STATE_WAITING_FOR_COOKIE,
@@ -173,8 +173,8 @@
         return doStart(callback);
     }
 
-    private boolean doStart(@NonNull BaseClientMonitor.Callback callback) {
-        final BaseClientMonitor.Callback cb = getWrappedCallback(callback);
+    private boolean doStart(@NonNull ClientMonitorCallback callback) {
+        final ClientMonitorCallback cb = getWrappedCallback(callback);
 
         if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
             Slog.d(TAG, "Operation marked for cancellation, cancelling now: " + this);
@@ -239,9 +239,9 @@
      *
      * @param handler handler to use for the cancellation watchdog
      * @param callback lifecycle callback (only used if this operation hasn't started, otherwise
-     *                 the callback used from {@link #start(BaseClientMonitor.Callback)} is used)
+     *                 the callback used from {@link #start(ClientMonitorCallback)} is used)
      */
-    public void cancel(@NonNull Handler handler, @NonNull BaseClientMonitor.Callback callback) {
+    public void cancel(@NonNull Handler handler, @NonNull ClientMonitorCallback callback) {
         checkNotInState("cancel", STATE_FINISHED);
 
         final int currentState = mState;
@@ -270,14 +270,14 @@
     }
 
     @NonNull
-    private BaseClientMonitor.Callback getWrappedCallback() {
+    private ClientMonitorCallback getWrappedCallback() {
         return getWrappedCallback(null);
     }
 
     @NonNull
-    private BaseClientMonitor.Callback getWrappedCallback(
-            @Nullable BaseClientMonitor.Callback callback) {
-        final BaseClientMonitor.Callback destroyCallback = new BaseClientMonitor.Callback() {
+    private ClientMonitorCallback getWrappedCallback(
+            @Nullable ClientMonitorCallback callback) {
+        final ClientMonitorCallback destroyCallback = new ClientMonitorCallback() {
             @Override
             public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                     boolean success) {
@@ -286,7 +286,7 @@
                 mState = STATE_FINISHED;
             }
         };
-        return new BaseClientMonitor.CompositeCallback(destroyCallback, callback, mClientCallback);
+        return new ClientMonitorCompositeCallback(destroyCallback, callback, mClientCallback);
     }
 
     /** {@link BaseClientMonitor#getSensorId()}. */
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
new file mode 100644
index 0000000..8ea4ee9
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallback.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+/**
+ * Interface that ClientMonitor holders should use to receive callbacks.
+ */
+public interface ClientMonitorCallback {
+    /**
+     * Invoked when the ClientMonitor operation has been started (e.g. reached the head of
+     * the queue and becomes the current operation).
+     *
+     * @param clientMonitor Reference of the ClientMonitor that is starting.
+     */
+    default void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {}
+
+    /**
+     * Invoked when the ClientMonitor operation is complete. This abstracts away asynchronous
+     * (i.e. Authenticate, Enroll, Enumerate, Remove) and synchronous (i.e. generateChallenge,
+     * revokeChallenge) so that a scheduler can process ClientMonitors regardless of their
+     * implementation.
+     *
+     * @param clientMonitor Reference of the ClientMonitor that finished.
+     * @param success       True if the operation completed successfully.
+     */
+    default void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {}
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
new file mode 100644
index 0000000..b82f5fa
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCompositeCallback.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import android.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Holder for wrapping multiple handlers into a single Callback. */
+public class ClientMonitorCompositeCallback implements ClientMonitorCallback {
+    @NonNull
+    private final List<ClientMonitorCallback> mCallbacks;
+
+    public ClientMonitorCompositeCallback(@NonNull ClientMonitorCallback... callbacks) {
+        mCallbacks = new ArrayList<>();
+
+        for (ClientMonitorCallback callback : callbacks) {
+            if (callback != null) {
+                mCallbacks.add(callback);
+            }
+        }
+    }
+
+    @Override
+    public final void onClientStarted(@NonNull BaseClientMonitor 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.size() - 1; i >= 0; i--) {
+            mCallbacks.get(i).onClientFinished(clientMonitor, success);
+        }
+    }
+}
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 c83323a..3b7adc1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -98,7 +98,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (hasReachedEnrollmentLimit()) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
index c2f909b..3060f30 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollmentModifier.java
@@ -23,7 +23,7 @@
 
     /**
      * Callers should typically check this after
-     * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)}
+     * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)}
      *
      * @return true if the user has gone from:
      *      1) none-enrolled --> enrolled
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 3d74f36..6fb6d08 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -47,7 +47,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
index 63cd412..c8830f8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java
@@ -45,7 +45,7 @@
     /**
      * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
      * If such a problem is detected, the scheduler will not invoke
-     * {@link #start(Callback)}.
+     * {@link #start(ClientMonitorCallback)}.
      */
     public abstract void unableToStart();
 
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 82a8437..0636893 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -64,7 +64,7 @@
     private final boolean mHasEnrollmentsBeforeStarting;
     private BaseClientMonitor mCurrentTask;
 
-    private final Callback mEnumerateCallback = new Callback() {
+    private final ClientMonitorCallback mEnumerateCallback = new ClientMonitorCallback() {
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
@@ -90,7 +90,7 @@
         }
     };
 
-    private final Callback mRemoveCallback = new Callback() {
+    private final ClientMonitorCallback mRemoveCallback = new ClientMonitorCallback() {
         @Override
         public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) {
             Slog.d(TAG, "Remove onClientFinished: " + clientMonitor + ", success: " + success);
@@ -139,7 +139,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         // Start enumeration. Removal will start if necessary, when enumeration is completed.
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index ced464e..05ea19a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -72,7 +72,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         // The biometric template ids will be removed when we get confirmation from the HAL
diff --git a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
index d5093c75..4f645ef 100644
--- a/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
+++ b/services/core/java/com/android/server/biometrics/sensors/Interruptable.java
@@ -29,15 +29,15 @@
 
     /**
      * Notifies the client that it needs to finish before
-     * {@link BaseClientMonitor#start(BaseClientMonitor.Callback)} was invoked. This usually happens
+     * {@link BaseClientMonitor#start(ClientMonitorCallback)} was invoked. This usually happens
      * if the client is still waiting in the pending queue and got notified that a subsequent
      * operation is preempting it.
      *
      * This method must invoke
-     * {@link BaseClientMonitor.Callback#onClientFinished(BaseClientMonitor, boolean)} on the
+     * {@link ClientMonitorCallback#onClientFinished(BaseClientMonitor, boolean)} on the
      * given callback (with success).
      *
      * @param callback invoked when the operation is completed.
      */
-    void cancelWithoutStarting(@NonNull BaseClientMonitor.Callback callback);
+    void cancelWithoutStarting(@NonNull ClientMonitorCallback callback);
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
index cede4a7..ee6bb0f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java
@@ -62,7 +62,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
index 5ba1b00..b2661a2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java
@@ -84,7 +84,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         mUtils.setInvalidationInProgress(getContext(), getTargetUserId(), true /* inProgress */);
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 2a6677e..e79819b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -17,7 +17,6 @@
 package com.android.server.biometrics.sensors;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricsProtoEnums;
@@ -59,7 +58,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         // The biometric template ids will be removed when we get confirmation from the HAL
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 1edf5af..21a6ddf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -38,7 +38,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
index 603cc22..4f90020 100644
--- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java
@@ -56,7 +56,7 @@
     @NonNull private final UserSwitchCallback mUserSwitchCallback;
     @Nullable private StopUserClient<?> mStopUserClient;
 
-    private class ClientFinishedCallback implements BaseClientMonitor.Callback {
+    private class ClientFinishedCallback implements ClientMonitorCallback {
         private final BaseClientMonitor mOwner;
 
         ClientFinishedCallback(BaseClientMonitor owner) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 77e431c..1e9b72b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -29,7 +29,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Surface;
 
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
 
@@ -137,7 +137,7 @@
     void startPreparedClient(int sensorId, int cookie);
 
     void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback);
+            @Nullable ClientMonitorCallback callback);
 
     void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto,
             boolean clearSchedulerBuffer);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 66b942b..8998269 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -35,6 +35,7 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.util.HashSet;
@@ -221,7 +222,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         Slog.d(TAG, "cleanupInternalState: " + userId);
-        mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index 757a52cb..dc21a04f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -39,7 +39,9 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutConsumer;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -97,15 +99,15 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         mState = STATE_STARTED;
     }
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
                 getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 2158dfe..72a20db07 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -30,6 +30,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.DetectionConsumer;
 
@@ -58,7 +59,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index b5f89b4..5c57dbb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -41,7 +41,9 @@
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.face.FaceService;
 import com.android.server.biometrics.sensors.face.FaceUtils;
@@ -67,8 +69,8 @@
     private final int mMaxTemplatesPerUser;
     private final boolean mDebugConsent;
 
-    private final BaseClientMonitor.Callback mPreviewHandleDeleterCallback =
-            new BaseClientMonitor.Callback() {
+    private final ClientMonitorCallback mPreviewHandleDeleterCallback =
+            new ClientMonitorCallback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 }
@@ -101,7 +103,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         BiometricNotificationUtils.cancelReEnrollNotification(getContext());
@@ -109,8 +111,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(mPreviewHandleDeleterCallback,
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(mPreviewHandleDeleterCallback,
                 getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
index af826c2..584b58c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.Map;
@@ -48,7 +49,7 @@
         // Nothing to do here
     }
 
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
index 315ede8b..acf5720 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java
@@ -29,6 +29,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -60,7 +61,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index ae507ab..9d7a552 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -49,6 +49,7 @@
 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.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.InvalidationRequesterClient;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -217,7 +218,7 @@
     }
 
     private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
-            BaseClientMonitor.Callback callback) {
+            ClientMonitorCallback callback) {
         if (!mSensors.contains(sensorId)) {
             throw new IllegalStateException("Unable to schedule client: " + client
                     + " for sensor: " + sensorId);
@@ -341,7 +342,7 @@
                     opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures,
                     ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
                     debugConsent);
-            scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
+            scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
@@ -511,7 +512,7 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             final List<Face> enrolledList = getEnrolledFaces(sensorId, userId);
             final FaceInternalCleanupClient client =
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
index 1e1b532..fd44c5c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java
@@ -27,6 +27,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutCache;
@@ -64,7 +65,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
index 4515d04..ee6982a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java
@@ -28,6 +28,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -65,7 +66,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
index 2b5f495..4a3da0d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StartUserClient;
 
 public class FaceStartUserClient extends StartUserClient<IFace, ISession> {
@@ -43,7 +44,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
index 06328e3..88b9235 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java
@@ -24,6 +24,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StopUserClient;
 
 public class FaceStopUserClient extends StopUserClient<ISession> {
@@ -36,7 +37,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index b45578b..e7483b3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -32,6 +32,7 @@
 
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.face.FaceUtils;
 
 import java.util.ArrayList;
@@ -197,7 +198,7 @@
     public void cleanupInternalState(int userId) {
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
-        mFace10.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mFace10.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index e957794..9a52db1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -60,6 +60,7 @@
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.EnumerateConsumer;
 import com.android.server.biometrics.sensors.ErrorConsumer;
@@ -534,7 +535,7 @@
                     mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
                     opPackageName, mSensorId, sSystemClock.millis());
             mGeneratedChallengeCache = client;
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                     if (client != clientMonitor) {
@@ -562,7 +563,7 @@
 
             final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
                     mLazyDaemon, token, userId, opPackageName, mSensorId);
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
@@ -591,7 +592,7 @@
                     opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures,
                     ENROLL_TIMEOUT_SEC, previewSurface, mSensorId);
 
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                         boolean success) {
@@ -742,7 +743,7 @@
             final int faceId = faces.get(0).getBiometricId();
             final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon,
                     token, listener, userId, opPackageName, mSensorId, feature, faceId);
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientFinished(
                         @NonNull BaseClientMonitor clientMonitor, boolean success) {
@@ -760,7 +761,7 @@
     }
 
     private void scheduleInternalCleanup(int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -774,7 +775,7 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         scheduleInternalCleanup(userId, callback);
     }
 
@@ -890,7 +891,7 @@
         final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
                 mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId,
                 hasEnrolled, mAuthenticatorIds);
-        mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+        mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
             @Override
             public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                     boolean success) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 80faf3e..1e0e799 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -34,7 +34,9 @@
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.face.UsageStats;
 
@@ -87,15 +89,15 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         mState = STATE_STARTED;
     }
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
                 getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
index 5c69d6f..8068e14 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java
@@ -33,7 +33,9 @@
 import com.android.internal.R;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 
 import java.util.ArrayList;
@@ -69,8 +71,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
                 getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
index f418104..e29a192 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.GenerateChallengeClient;
 
@@ -39,7 +40,7 @@
 
     private static final String TAG = "FaceGenerateChallengeClient";
     static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
-    private static final Callback EMPTY_CALLBACK = new Callback() {
+    private static final ClientMonitorCallback EMPTY_CALLBACK = new ClientMonitorCallback() {
     };
 
     private final long mCreatedAt;
@@ -94,7 +95,7 @@
     }
 
     private void sendChallengeResult(@NonNull ClientMonitorCallbackConverter receiver,
-            @NonNull Callback ownerCallback) {
+            @NonNull ClientMonitorCallback ownerCallback) {
         Preconditions.checkState(mChallengeResult != null, "result not available");
         try {
             receiver.onChallengeGenerated(getSensorId(), getTargetUserId(), mChallengeResult);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
index 7821601..0a9d96d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
@@ -66,7 +67,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
index 9d977d6..ee01c43 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.ArrayList;
@@ -57,7 +58,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
index cc3d8f0..ee28f7b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java
@@ -26,6 +26,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
@@ -71,7 +72,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         startHalOperation();
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
index 5343d0d..8ee8ce5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.io.File;
@@ -49,7 +50,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 be0e6ed..04fd534 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
@@ -31,6 +31,7 @@
 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.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.EnrollmentModifier;
 
@@ -39,7 +40,7 @@
 /**
  * A callback for receiving notifications about changes in fingerprint state.
  */
-public class FingerprintStateCallback implements BaseClientMonitor.Callback {
+public class FingerprintStateCallback implements ClientMonitorCallback {
 
     @NonNull private final CopyOnWriteArrayList<IFingerprintStateListener>
             mFingerprintStateListeners = new CopyOnWriteArrayList<>();
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 535705c..0bdc4eb 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
@@ -30,7 +30,7 @@
 import android.os.IBinder;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.LockoutTracker;
 
@@ -121,7 +121,7 @@
             @NonNull String opPackageName);
 
     void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback);
+            @Nullable ClientMonitorCallback callback);
 
     boolean isHardwareDetected(int sensorId);
 
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 2b50b96..b29fbb6 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
@@ -32,6 +32,7 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 
@@ -204,7 +205,7 @@
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
         Slog.d(TAG, "cleanupInternalState: " + userId);
-        mProvider.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mProvider.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index 96f4853..f3d0121 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -37,7 +37,9 @@
 import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutCache;
 import com.android.server.biometrics.sensors.LockoutConsumer;
 import com.android.server.biometrics.sensors.LockoutTracker;
@@ -86,7 +88,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (mSensorProps.isAnyUdfpsType()) {
@@ -99,8 +101,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(mALSProbeCallback, callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
index ac3ce89..1f0482d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java
@@ -30,6 +30,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.DetectionConsumer;
 import com.android.server.biometrics.sensors.SensorOverlays;
@@ -61,7 +62,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
index e3f26df..169c3eb 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java
@@ -37,7 +37,9 @@
 import com.android.server.biometrics.HardwareAuthTokenUtils;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.SensorOverlays;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
@@ -82,8 +84,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
                 getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
index ed2345e..52bd234 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java
@@ -24,6 +24,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.util.Map;
@@ -48,7 +49,7 @@
         // Nothing to do here
     }
 
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 eb16c76..efc9304 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
@@ -55,7 +55,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.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.InvalidationRequesterClient;
 import com.android.server.biometrics.sensors.LockoutResetDispatcher;
 import com.android.server.biometrics.sensors.PerformanceTracker;
@@ -248,7 +250,7 @@
     }
 
     private void scheduleForSensor(int sensorId, @NonNull BaseClientMonitor client,
-            BaseClientMonitor.Callback callback) {
+            ClientMonitorCallback callback) {
         if (!mSensors.contains(sensorId)) {
             throw new IllegalStateException("Unable to schedule client: " + client
                     + " for sensor: " + sensorId);
@@ -361,7 +363,7 @@
                     opPackageName, FingerprintUtils.getInstance(sensorId), sensorId,
                     mSensors.get(sensorId).getSensorProperties(),
                     mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason);
-            scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
+            scheduleForSensor(sensorId, client, new ClientMonitorCallback() {
 
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
@@ -484,7 +486,7 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId);
             final FingerprintInternalCleanupClient client =
@@ -493,7 +495,7 @@
                             mContext.getOpPackageName(), sensorId, enrolledList,
                             FingerprintUtils.getInstance(sensorId),
                             mSensors.get(sensorId).getAuthenticatorIds());
-            scheduleForSensor(sensorId, client, new BaseClientMonitor.CompositeCallback(callback,
+            scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback,
                     mFingerprintStateCallback));
         });
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
index 878ef46..ee8d170 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java
@@ -27,6 +27,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.HardwareAuthTokenUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 import com.android.server.biometrics.sensors.LockoutCache;
@@ -64,7 +65,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
index ee81620..9f11df6 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java
@@ -27,6 +27,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StartUserClient;
 
 public class FingerprintStartUserClient extends StartUserClient<IFingerprint, ISession> {
@@ -44,7 +45,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
index 7055d65..9d38145 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java
@@ -24,6 +24,7 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.StopUserClient;
 
 public class FingerprintStopUserClient extends StopUserClient<ISession> {
@@ -36,7 +37,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
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 79c6b1b3..033855f 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
@@ -31,6 +31,7 @@
 
 import com.android.server.biometrics.Utils;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintStateCallback;
 import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils;
 
@@ -201,7 +202,7 @@
     public void cleanupInternalState(int userId)  {
         Utils.checkPermission(mContext, TEST_BIOMETRIC);
 
-        mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new BaseClientMonitor.Callback() {
+        mFingerprint21.scheduleInternalCleanup(mSensorId, userId, new ClientMonitorCallback() {
             @Override
             public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                 try {
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 6feb5fa..f160dff 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
@@ -62,7 +62,9 @@
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
 import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnumerateConsumer;
 import com.android.server.biometrics.sensors.ErrorConsumer;
 import com.android.server.biometrics.sensors.HalClientMonitor;
@@ -492,7 +494,7 @@
                 new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId,
                         mContext.getOpPackageName(), mSensorProperties.sensorId,
                         this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force);
-        mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+        mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
             @Override
             public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
                     boolean success) {
@@ -577,7 +579,7 @@
                     FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC,
                     mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController,
                     enrollReason);
-            mScheduler.scheduleClientMonitor(client, new BaseClientMonitor.Callback() {
+            mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() {
                 @Override
                 public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
                     mFingerprintStateCallback.onClientStarted(clientMonitor);
@@ -699,7 +701,7 @@
     }
 
     private void scheduleInternalCleanup(int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
+            @Nullable ClientMonitorCallback callback) {
         mHandler.post(() -> {
             scheduleUpdateActiveUserWithoutHandler(userId);
 
@@ -715,8 +717,8 @@
 
     @Override
     public void scheduleInternalCleanup(int sensorId, int userId,
-            @Nullable BaseClientMonitor.Callback callback) {
-        scheduleInternalCleanup(userId, new BaseClientMonitor.CompositeCallback(callback,
+            @Nullable ClientMonitorCallback callback) {
+        scheduleInternalCleanup(userId, new ClientMonitorCompositeCallback(callback,
                 mFingerprintStateCallback));
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index d9b290f..87d47c1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -36,7 +36,9 @@
 import com.android.server.biometrics.log.Probe;
 import com.android.server.biometrics.sensors.AuthenticationClient;
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.LockoutTracker;
 import com.android.server.biometrics.sensors.SensorOverlays;
 import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -86,7 +88,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (mSensorProps.isAnyUdfpsType()) {
@@ -99,8 +101,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(mALSProbeCallback, callback);
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(mALSProbeCallback, callback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
index f1dec66..9137212 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java
@@ -32,6 +32,7 @@
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.AcquisitionClient;
 import com.android.server.biometrics.sensors.AuthenticationConsumer;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 import com.android.server.biometrics.sensors.PerformanceTracker;
 import com.android.server.biometrics.sensors.SensorOverlays;
@@ -82,7 +83,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
index dd92e3e..82b046d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java
@@ -33,7 +33,9 @@
 
 import com.android.server.biometrics.sensors.BiometricNotificationUtils;
 import com.android.server.biometrics.sensors.BiometricUtils;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
 import com.android.server.biometrics.sensors.EnrollClient;
 import com.android.server.biometrics.sensors.SensorOverlays;
 import com.android.server.biometrics.sensors.fingerprint.Udfps;
@@ -75,8 +77,8 @@
 
     @NonNull
     @Override
-    protected Callback wrapCallbackForStart(@NonNull Callback callback) {
-        return new CompositeCallback(
+    protected ClientMonitorCallback wrapCallbackForStart(@NonNull ClientMonitorCallback callback) {
+        return new ClientMonitorCompositeCallback(
                 getLogger().createALSCallback(true /* startWithClient */), callback);
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
index a39f4f8..ed28e3f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java
@@ -22,6 +22,7 @@
 
 import com.android.server.biometrics.BiometricsProto;
 import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 
 /**
  * Clears lockout, which is handled in the framework (and not the HAL) for the
@@ -40,7 +41,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
         mLockoutTracker.resetFailedAttemptsForUser(true /* clearAttemptCounter */,
                 getTargetUserId());
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
index a2c1892..d317984 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java
@@ -27,6 +27,7 @@
 import android.util.Slog;
 
 import com.android.server.biometrics.BiometricsProto;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.HalClientMonitor;
 
 import java.io.File;
@@ -62,7 +63,7 @@
     }
 
     @Override
-    public void start(@NonNull Callback callback) {
+    public void start(@NonNull ClientMonitorCallback callback) {
         super.start(callback);
 
         if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 06f56c9..4da26f6 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -97,7 +97,6 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IInterface;
 import android.os.LocaleList;
 import android.os.Message;
 import android.os.Parcel;
@@ -220,13 +219,8 @@
     }
 
     private static final int MSG_SHOW_IM_SUBTYPE_PICKER = 1;
-    private static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 2;
     private static final int MSG_SHOW_IM_CONFIG = 3;
 
-    private static final int MSG_UNBIND_INPUT = 1000;
-    private static final int MSG_BIND_INPUT = 1010;
-    private static final int MSG_SHOW_SOFT_INPUT = 1020;
-    private static final int MSG_HIDE_SOFT_INPUT = 1030;
     private static final int MSG_HIDE_CURRENT_INPUT_METHOD = 1035;
     private static final int MSG_INITIALIZE_IME = 1040;
     private static final int MSG_CREATE_SESSION = 1050;
@@ -785,7 +779,7 @@
             final int mFocusedWindowSoftInputMode;
             @SoftInputShowHideReason
             final int mReason;
-            // The timing of handling MSG_SHOW_SOFT_INPUT or MSG_HIDE_SOFT_INPUT.
+            // The timing of handling showCurrentInputLocked() or hideCurrentInputLocked().
             final long mTimestamp;
             final long mWallTime;
             final boolean mInFullscreenMode;
@@ -1575,7 +1569,7 @@
             mHandler.removeCallbacks(mUserSwitchHandlerTask);
         }
         // Hide soft input before user switch task since switch task may block main handler a while
-        // and delayed the MSG_HIDE_SOFT_INPUT.
+        // and delayed the hideCurrentInputLocked().
         hideCurrentInputLocked(
                 mCurFocusedWindow, 0, null, SoftInputShowHideReason.HIDE_SWITCH_USER);
         final UserSwitchHandlerTask task = new UserSwitchHandlerTask(this, userId,
@@ -2203,8 +2197,11 @@
                         mBoundToMethod = false;
                         IInputMethod curMethod = getCurMethodLocked();
                         if (curMethod != null) {
-                            executeOrSendMessage(curMethod, mCaller.obtainMessageO(
-                                    MSG_UNBIND_INPUT, curMethod));
+                            try {
+                                curMethod.unbindInput();
+                            } catch (RemoteException e) {
+                                // There is nothing interesting about the method dying.
+                            }
                         }
                     }
                     mCurClient = null;
@@ -2216,8 +2213,24 @@
         }
     }
 
-    private void executeOrSendMessage(IInterface target, Message msg) {
+    // TODO(b/215609403): This method will be removed soon!
+    private void executeOrSendMessage(IInputMethod target, Message msg) {
+        if (target.asBinder() instanceof Binder) {
+            throw new UnsupportedOperationException(
+                    "InputMethodService is not supported to run in the system_server");
+        }
+        handleMessage(msg);
+        msg.recycle();
+    }
+
+    private void executeOrSendMessage(IInputMethodClient target, Message msg) {
          if (target.asBinder() instanceof Binder) {
+             // This is supposed to be emulating the one-way semantics when the IME client is
+             // system_server itself, which has not been explicitly prohibited so far while we have
+             // never ever officially supported such a use case...
+             // We probably should create a simple wrapper of IInputMethodClient as the first step
+             // to get rid of executeOrSendMessage() then should prohibit system_server to be the
+             // IME client for long term.
              mCaller.sendMessage(msg);
          } else {
              handleMessage(msg);
@@ -2234,8 +2247,11 @@
                 mBoundToMethod = false;
                 IInputMethod curMethod = getCurMethodLocked();
                 if (curMethod != null) {
-                    executeOrSendMessage(curMethod, mCaller.obtainMessageO(
-                            MSG_UNBIND_INPUT, curMethod));
+                    try {
+                        curMethod.unbindInput();
+                    } catch (RemoteException e) {
+                        // There is nothing interesting about the method dying.
+                    }
                 }
             }
 
@@ -2285,8 +2301,10 @@
     InputBindResult attachNewInputLocked(@StartInputReason int startInputReason, boolean initial) {
         if (!mBoundToMethod) {
             IInputMethod curMethod = getCurMethodLocked();
-            executeOrSendMessage(curMethod, mCaller.obtainMessageOO(
-                    MSG_BIND_INPUT, curMethod, mCurClient.binding));
+            try {
+                curMethod.bindInput(mCurClient.binding);
+            } catch (RemoteException e) {
+            }
             mBoundToMethod = true;
         }
 
@@ -3112,18 +3130,25 @@
         }
 
         mBindingController.setCurrentMethodVisible();
-        if (getCurMethodLocked() != null) {
+        final IInputMethod curMethod = getCurMethodLocked();
+        if (curMethod != null) {
             // create a placeholder token for IMS so that IMS cannot inject windows into client app.
             Binder showInputToken = new Binder();
             mShowRequestWindowMap.put(showInputToken, windowToken);
-            IInputMethod curMethod = getCurMethodLocked();
-            executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
-                    getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
-                    showInputToken));
+            final int showFlags = getImeShowFlagsLocked();
+            try {
+                if (DEBUG) {
+                    Slog.v(TAG, "Calling " + curMethod + ".showSoftInput(" + showInputToken
+                            + ", " + showFlags + ", " + resultReceiver + ") for reason: "
+                            + InputMethodDebug.softInputDisplayReasonToString(reason));
+                }
+                curMethod.showSoftInput(showInputToken, showFlags, resultReceiver);
+                onShowHideSoftInputRequested(true /* show */, windowToken, reason);
+            } catch (RemoteException e) {
+            }
             mInputShown = true;
             return true;
         }
-
         return false;
     }
 
@@ -3202,8 +3227,16 @@
             // delivered to the IME process as an IPC.  Hence the inconsistency between
             // IMMS#mInputShown and IMMS#mImeWindowVis should be resolved spontaneously in
             // the final state.
-            executeOrSendMessage(curMethod, mCaller.obtainMessageIOOO(MSG_HIDE_SOFT_INPUT,
-                    reason, curMethod, resultReceiver, hideInputToken));
+            if (DEBUG) {
+                Slog.v(TAG, "Calling " + curMethod + ".hideSoftInput(0, " + hideInputToken
+                        + ", " + resultReceiver + ") for reason: "
+                        + InputMethodDebug.softInputDisplayReasonToString(reason));
+            }
+            try {
+                curMethod.hideSoftInput(hideInputToken, 0 /* flags */, resultReceiver);
+                onShowHideSoftInputRequested(false /* show */, windowToken, reason);
+            } catch (RemoteException e) {
+            }
             res = true;
         } else {
             res = false;
@@ -3679,8 +3712,7 @@
             if (!calledFromValidUserLocked()) {
                 return;
             }
-            executeOrSendMessage(getCurMethodLocked(), mCaller.obtainMessageO(
-                    MSG_SHOW_IM_SUBTYPE_ENABLER, inputMethodId));
+            showInputMethodAndSubtypeEnabler(inputMethodId);
         }
     }
 
@@ -4203,69 +4235,12 @@
                 mMenuController.showInputMethodMenu(showAuxSubtypes, displayId);
                 return true;
 
-            case MSG_SHOW_IM_SUBTYPE_ENABLER:
-                showInputMethodAndSubtypeEnabler((String)msg.obj);
-                return true;
-
             case MSG_SHOW_IM_CONFIG:
                 showConfigureInputMethods();
                 return true;
 
             // ---------------------------------------------------------
 
-            case MSG_UNBIND_INPUT:
-                try {
-                    ((IInputMethod)msg.obj).unbindInput();
-                } catch (RemoteException e) {
-                    // There is nothing interesting about the method dying.
-                }
-                return true;
-            case MSG_BIND_INPUT:
-                args = (SomeArgs)msg.obj;
-                try {
-                    ((IInputMethod)args.arg1).bindInput((InputBinding)args.arg2);
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
-            case MSG_SHOW_SOFT_INPUT:
-                args = (SomeArgs) msg.obj;
-                try {
-                    final @SoftInputShowHideReason int reason = msg.arg2;
-                    if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".showSoftInput("
-                            + args.arg3 + ", " + msg.arg1 + ", " + args.arg2 + ") for reason: "
-                            + InputMethodDebug.softInputDisplayReasonToString(reason));
-                    final IBinder token = (IBinder) args.arg3;
-                    ((IInputMethod) args.arg1).showSoftInput(
-                            token, msg.arg1 /* flags */, (ResultReceiver) args.arg2);
-                    final IBinder requestToken;
-                    synchronized (ImfLock.class) {
-                        requestToken = mShowRequestWindowMap.get(token);
-                        onShowHideSoftInputRequested(true /* show */, requestToken, reason);
-                    }
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
-            case MSG_HIDE_SOFT_INPUT:
-                args = (SomeArgs) msg.obj;
-                try {
-                    final @SoftInputShowHideReason int reason = msg.arg1;
-                    if (DEBUG) Slog.v(TAG, "Calling " + args.arg1 + ".hideSoftInput(0, "
-                            + args.arg3 + ", " + args.arg2 + ") for reason: "
-                            + InputMethodDebug.softInputDisplayReasonToString(reason));
-                    final IBinder token = (IBinder) args.arg3;
-                    ((IInputMethod)args.arg1).hideSoftInput(
-                            token, 0 /* flags */, (ResultReceiver) args.arg2);
-                    final IBinder requestToken;
-                    synchronized (ImfLock.class) {
-                        requestToken = mHideRequestWindowMap.get(token);
-                        onShowHideSoftInputRequested(false /* show */, requestToken, reason);
-                    }
-                } catch (RemoteException e) {
-                }
-                args.recycle();
-                return true;
             case MSG_HIDE_CURRENT_INPUT_METHOD:
                 synchronized (ImfLock.class) {
                     final @SoftInputShowHideReason int reason = (int) msg.obj;
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index ffc1aed4..91de9e5 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -344,10 +344,19 @@
     }
 
     private void addActiveRoute(BluetoothRouteInfo btRoute) {
+        if (btRoute == null) {
+            if (DEBUG) {
+                Log.d(TAG, " btRoute is null");
+            }
+            return;
+        }
         if (DEBUG) {
             Log.d(TAG, "Adding active route: " + btRoute.route);
         }
-        if (btRoute == null || mActiveRoutes.contains(btRoute)) {
+        if (mActiveRoutes.contains(btRoute)) {
+            if (DEBUG) {
+                Log.d(TAG, " btRoute is already added.");
+            }
             return;
         }
         setRouteConnectionState(btRoute, STATE_CONNECTED);
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 6f10a6b..2e9ad50 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -45,6 +45,7 @@
 import android.sysprop.ApexProperties;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.PrintWriterPrinter;
 import android.util.Singleton;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -1164,6 +1165,10 @@
                 ipw.println("Path: " + pi.applicationInfo.sourceDir);
                 ipw.println("IsActive: " + isActive(pi));
                 ipw.println("IsFactory: " + isFactory(pi));
+                ipw.println("ApplicationInfo: ");
+                ipw.increaseIndent();
+                pi.applicationInfo.dump(new PrintWriterPrinter(ipw), "");
+                ipw.decreaseIndent();
                 ipw.decreaseIndent();
             }
             ipw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1cfcdf51..8e16835 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -558,9 +558,9 @@
     }
 
     @Override
-    public void selfRevokePermissions(@NonNull String packageName,
+    public void revokeOwnPermissionsOnKill(@NonNull String packageName,
             @NonNull List<String> permissions) {
-        mPermissionManagerServiceImpl.selfRevokePermissions(packageName, permissions);
+        mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 981fd8e..d639f7d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -1592,7 +1592,7 @@
     }
 
     @Override
-    public void selfRevokePermissions(String packageName, List<String> permissions) {
+    public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions) {
         final int callingUid = Binder.getCallingUid();
         int callingUserId = UserHandle.getUserId(callingUid);
         int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId);
@@ -1607,7 +1607,7 @@
                         + permName + " because it does not hold that permission");
             }
         }
-        mPermissionControllerManager.selfRevokePermissions(packageName, permissions);
+        mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions);
     }
 
     private boolean mayManageRolePermission(int uid) {
@@ -3181,9 +3181,13 @@
                     ps.updatePermissionFlags(bp, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
                                     | FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
                             PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED);
-                    // TODO(b/205888750): remove revoke once propagated through droidfood
-                    if (ps.isPermissionGranted(newPerm)) {
+                    // TODO(b/205888750): remove if/else block once propagated through droidfood
+                    if (ps.isPermissionGranted(newPerm)
+                            && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
                         ps.revokePermission(bp);
+                    } else if (!ps.isPermissionGranted(newPerm)
+                            && pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) {
+                        ps.grantPermission(bp);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index c582f9e..b558e3d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -344,7 +344,7 @@
      * @param packageName The name of the package for which the permissions will be revoked.
      * @param permissions List of permissions to be revoked.
      */
-    void selfRevokePermissions(String packageName, List<String> permissions);
+    void revokeOwnPermissionsOnKill(String packageName, List<String> permissions);
 
     /**
      * Get whether you should show UI with rationale for requesting a permission. You should do this
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 17f5566..0edd06a 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -22,6 +22,7 @@
 import static android.app.StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
 import static android.app.StatusBarManager.NavBarModeOverride;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -38,6 +39,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.om.IOverlayManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
@@ -60,6 +62,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -79,6 +82,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.statusbar.IAddTileResultCallback;
@@ -154,6 +158,8 @@
     private final ArrayMap<String, Long> mCurrentRequestAddTilePackages = new ArrayMap<>();
     private static final long REQUEST_TIME_OUT = TimeUnit.MINUTES.toNanos(5);
 
+    private IOverlayManager mOverlayManager;
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         public void binderDied() {
             mBar.asBinder().unlinkToDeath(this,0);
@@ -256,6 +262,18 @@
         mTileRequestTracker = new TileRequestTracker(mContext);
     }
 
+    private IOverlayManager getOverlayManager() {
+        // No need to synchronize; worst-case scenario it will be fetched twice.
+        if (mOverlayManager == null) {
+            mOverlayManager = IOverlayManager.Stub.asInterface(
+                    ServiceManager.getService(Context.OVERLAY_SERVICE));
+            if (mOverlayManager == null) {
+                Slog.w("StatusBarManager", "warning: no OVERLAY_SERVICE");
+            }
+        }
+        return mOverlayManager;
+    }
+
     @Override
     public void onDisplayAdded(int displayId) {}
 
@@ -1296,6 +1314,11 @@
         });
     }
 
+    @VisibleForTesting
+    void registerOverlayManager(IOverlayManager overlayManager) {
+        mOverlayManager = overlayManager;
+    }
+
     /**
      * @param clearNotificationEffects whether to consider notifications as "shown" and stop
      *     LED, vibration, and ringing
@@ -1869,6 +1892,14 @@
         try {
             Settings.Secure.putIntForUser(mContext.getContentResolver(),
                     Settings.Secure.NAV_BAR_KIDS_MODE, navBarModeOverride, userId);
+
+            IOverlayManager overlayManager = getOverlayManager();
+            if (overlayManager != null && navBarModeOverride == NAV_BAR_MODE_OVERRIDE_KIDS
+                    && isPackageSupported(NAV_BAR_MODE_3BUTTON_OVERLAY)) {
+                overlayManager.setEnabledExclusiveInCategory(NAV_BAR_MODE_3BUTTON_OVERLAY, userId);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         } finally {
             Binder.restoreCallingIdentity(userIdentity);
         }
@@ -1896,6 +1927,21 @@
         return navBarKidsMode;
     }
 
+    private boolean isPackageSupported(String packageName) {
+        if (packageName == null) {
+            return false;
+        }
+        try {
+            return mContext.getPackageManager().getPackageInfo(packageName,
+                    PackageManager.PackageInfoFlags.of(0)) != null;
+        } catch (PackageManager.NameNotFoundException ignored) {
+            if (SPEW) {
+                Slog.d(TAG, "Package not found: " + packageName);
+            }
+        }
+        return false;
+    }
+
     /** @hide */
     public void passThroughShellCommand(String[] args, FileDescriptor fd) {
         enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 3093509..c0207f0 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -37,6 +37,7 @@
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.provider.DeviceConfig;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.DataUnit;
@@ -255,11 +256,14 @@
     private void checkHigh() {
         final StorageManager storage = getContext().getSystemService(StorageManager.class);
         // Check every mounted private volume to see if they're under the high storage threshold
-        // which is StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space
+        // which is storageThresholdPercentHigh of total space
+        final int storageThresholdPercentHigh = DeviceConfig.getInt(
+                DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY,
+                StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
         for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
             final File file = vol.getPath();
-            if (file.getUsableSpace() < file.getTotalSpace()
-                    * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
+            if (file.getUsableSpace() < file.getTotalSpace() * storageThresholdPercentHigh / 100) {
                 final PackageManagerService pms = (PackageManagerService) ServiceManager
                         .getService("package");
                 try {
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
similarity index 98%
rename from services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
rename to services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index 6058d88..35cc43f 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -34,15 +34,15 @@
 import android.media.tv.BroadcastInfoRequest;
 import android.media.tv.BroadcastInfoResponse;
 import android.media.tv.TvTrackInfo;
-import android.media.tv.interactive.ITvIAppManager;
 import android.media.tv.interactive.ITvInteractiveAppClient;
+import android.media.tv.interactive.ITvInteractiveAppManager;
 import android.media.tv.interactive.ITvInteractiveAppManagerCallback;
 import android.media.tv.interactive.ITvInteractiveAppService;
 import android.media.tv.interactive.ITvInteractiveAppServiceCallback;
 import android.media.tv.interactive.ITvInteractiveAppSession;
 import android.media.tv.interactive.ITvInteractiveAppSessionCallback;
-import android.media.tv.interactive.TvIAppService;
 import android.media.tv.interactive.TvInteractiveAppInfo;
+import android.media.tv.interactive.TvInteractiveAppService;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -79,9 +79,9 @@
 /**
  * This class provides a system service that manages interactive TV applications.
  */
-public class TvIAppManagerService extends SystemService {
+public class TvInteractiveAppManagerService extends SystemService {
     private static final boolean DEBUG = false;
-    private static final String TAG = "TvIAppManagerService";
+    private static final String TAG = "TvInteractiveAppManagerService";
     // A global lock.
     private final Object mLock = new Object();
     private final Context mContext;
@@ -106,7 +106,7 @@
      *
      * @param context The system server context.
      */
-    public TvIAppManagerService(Context context) {
+    public TvInteractiveAppManagerService(Context context) {
         super(context);
         mContext = context;
         mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
@@ -122,7 +122,7 @@
         }
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> services = pm.queryIntentServicesAsUser(
-                new Intent(TvIAppService.SERVICE_INTERFACE),
+                new Intent(TvInteractiveAppService.SERVICE_INTERFACE),
                 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
                 userId);
         List<TvInteractiveAppInfo> iAppList = new ArrayList<>();
@@ -256,15 +256,16 @@
 
     @GuardedBy("mLock")
     private void notifyStateChangedLocked(
-            UserState userState, String iAppServiceId, int type, int state) {
+            UserState userState, String iAppServiceId, int type, int state, int err) {
         if (DEBUG) {
             Slog.d(TAG, "notifyRteStateChanged(iAppServiceId="
-                    + iAppServiceId + ", type=" + type + ", state=" + state + ")");
+                    + iAppServiceId + ", type=" + type + ", state=" + state + ", err=" + err + ")");
         }
         int n = userState.mCallbacks.beginBroadcast();
         for (int i = 0; i < n; ++i) {
             try {
-                userState.mCallbacks.getBroadcastItem(i).onStateChanged(iAppServiceId, type, state);
+                userState.mCallbacks.getBroadcastItem(i)
+                        .onStateChanged(iAppServiceId, type, state, err);
             } catch (RemoteException e) {
                 Slog.e(TAG, "failed to report RTE state changed", e);
             }
@@ -287,7 +288,7 @@
         if (DEBUG) {
             Slogf.d(TAG, "onStart");
         }
-        publishBinderService(Context.TV_IAPP_SERVICE, new BinderService());
+        publishBinderService(Context.TV_INTERACTIVE_APP_SERVICE, new BinderService());
     }
 
     @Override
@@ -628,7 +629,7 @@
         return session;
     }
 
-    private final class BinderService extends ITvIAppManager.Stub {
+    private final class BinderService extends ITvInteractiveAppManager.Stub {
 
         @Override
         public List<TvInteractiveAppInfo> getTvInteractiveAppServiceList(int userId) {
@@ -1361,7 +1362,7 @@
                 }
             } finally {
                 if (surface != null) {
-                    // surface is not used in TvIAppManagerService.
+                    // surface is not used in TvInteractiveAppManagerService.
                     surface.release();
                 }
                 Binder.restoreCallingIdentity(identity);
@@ -1678,7 +1679,7 @@
             }
 
             Intent i =
-                    new Intent(TvIAppService.SERVICE_INTERFACE).setComponent(component);
+                    new Intent(TvInteractiveAppService.SERVICE_INTERFACE).setComponent(component);
             serviceState.mBound = mContext.bindServiceAsUser(
                     i, serviceState.mConnection,
                     Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
@@ -1866,6 +1867,16 @@
                 ServiceState serviceState = userState.mServiceStateMap.get(mComponent);
                 serviceState.mService = ITvInteractiveAppService.Stub.asInterface(service);
 
+                // Register a callback, if we need to.
+                if (serviceState.mCallback == null) {
+                    serviceState.mCallback = new ServiceCallback(mComponent, mUserId);
+                    try {
+                        serviceState.mService.registerCallback(serviceState.mCallback);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in registerCallback", e);
+                    }
+                }
+
                 if (serviceState.mPendingPrepare) {
                     final long identity = Binder.clearCallingIdentity();
                     try {
@@ -1968,14 +1979,14 @@
         }
 
         @Override
-        public void onStateChanged(int type, int state) {
+        public void onStateChanged(int type, int state, int error) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
                     String iAppServiceId = serviceState.mIAppServiceId;
                     UserState userState = getUserStateLocked(mUserId);
-                    notifyStateChangedLocked(userState, iAppServiceId, type, state);
+                    notifyStateChangedLocked(userState, iAppServiceId, type, state, error);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -2072,7 +2083,7 @@
 
         @Override
         public void onCommandRequest(
-                @TvIAppService.InteractiveAppServiceCommandType String cmdType,
+                @TvInteractiveAppService.InteractiveAppServiceCommandType String cmdType,
                 Bundle parameters) {
             synchronized (mLock) {
                 if (DEBUG) {
@@ -2210,16 +2221,16 @@
         }
 
         @Override
-        public void onSessionStateChanged(int state) {
+        public void onSessionStateChanged(int state, int err) {
             synchronized (mLock) {
                 if (DEBUG) {
-                    Slogf.d(TAG, "onSessionStateChanged (state=" + state + ")");
+                    Slogf.d(TAG, "onSessionStateChanged (state=" + state + ", err=" + err + ")");
                 }
                 if (mSessionState.mSession == null || mSessionState.mClient == null) {
                     return;
                 }
                 try {
-                    mSessionState.mClient.onSessionStateChanged(state, mSessionState.mSeq);
+                    mSessionState.mClient.onSessionStateChanged(state, err, mSessionState.mSeq);
                 } catch (RemoteException e) {
                     Slogf.e(TAG, "error in onSessionStateChanged", e);
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ddd624d..ed9dcef 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -221,10 +221,10 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationRunner;
-import android.view.IRemoteAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.WindowManager;
+import android.window.BackNavigationInfo;
 import android.window.IWindowOrganizerController;
 import android.window.SplashScreenView.SplashScreenViewParcelable;
 import android.window.TaskSnapshot;
@@ -458,7 +458,7 @@
     private final ClientLifecycleManager mLifecycleManager;
 
     @Nullable
-    private final BackGestureController mBackGestureController;
+    private final BackNavigationController mBackNavigationController;
 
     private TaskChangeNotificationController mTaskChangeNotificationController;
     /** The controller for all operations related to locktask. */
@@ -836,8 +836,6 @@
         mSystemThread = ActivityThread.currentActivityThread();
         mUiContext = mSystemThread.getSystemUiContext();
         mLifecycleManager = new ClientLifecycleManager();
-        mBackGestureController = BackGestureController.isEnabled() ? new BackGestureController()
-                : null;
         mVisibleActivityProcessTracker = new VisibleActivityProcessTracker(this);
         mInternal = new LocalService();
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", GL_ES_VERSION_UNDEFINED);
@@ -845,6 +843,8 @@
         mTaskOrganizerController = mWindowOrganizerController.mTaskOrganizerController;
         mTaskFragmentOrganizerController =
                 mWindowOrganizerController.mTaskFragmentOrganizerController;
+        mBackNavigationController = BackNavigationController.isEnabled()
+                ? new BackNavigationController() : null;
     }
 
     public void onSystemReady() {
@@ -1022,6 +1022,9 @@
             mLockTaskController.setWindowManager(wm);
             mTaskSupervisor.setWindowManager(wm);
             mRootWindowContainer.setWindowManager(wm);
+            if (mBackNavigationController != null) {
+                mBackNavigationController.setTaskSnapshotController(wm.mTaskSnapshotController);
+            }
         }
     }
 
@@ -1768,11 +1771,13 @@
     }
 
     @Override
-    public void startBackPreview(IRemoteAnimationRunner runner) {
-        if (mBackGestureController == null) {
-            return;
+    public BackNavigationInfo startBackNavigation() {
+        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+                "startBackNavigation()");
+        if (mBackNavigationController == null) {
+            return null;
         }
-        mBackGestureController.startBackPreview();
+        return mBackNavigationController.startBackNavigation(getTopDisplayFocusedRootTask());
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/BackGestureController.java b/services/core/java/com/android/server/wm/BackGestureController.java
deleted file mode 100644
index f8f6254..0000000
--- a/services/core/java/com/android/server/wm/BackGestureController.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.wm;
-
-import android.os.SystemProperties;
-
-/**
- * Controller to handle actions related to the back gesture on the server side.
- */
-public class BackGestureController {
-
-    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
-
-    public static boolean isEnabled() {
-        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
-    }
-
-    /**
-     * Start a remote animation the back gesture.
-     */
-    public void startBackPreview() {
-    }
-}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
new file mode 100644
index 0000000..a8779fa
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -0,0 +1,240 @@
+/*
+ * 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.wm;
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.WindowConfiguration;
+import android.content.ComponentName;
+import android.hardware.HardwareBuffer;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Slog;
+import android.view.SurfaceControl;
+import android.window.BackNavigationInfo;
+import android.window.TaskSnapshot;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
+
+/**
+ * Controller to handle actions related to the back gesture on the server side.
+ */
+class BackNavigationController {
+
+    private static final String TAG = "BackNavigationController";
+    private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+
+    @Nullable
+    private TaskSnapshotController mTaskSnapshotController;
+
+    /**
+     * Returns true if the back predictability feature is enabled
+     */
+    static boolean isEnabled() {
+        return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
+    }
+
+    /**
+     * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
+     * back gesture animation.
+     *
+     * @param task the currently focused {@link Task}.
+     * @return a {@link BackNavigationInfo} instance containing the required leashes and metadata
+     * for the animation.
+     */
+    @Nullable
+    BackNavigationInfo startBackNavigation(@NonNull Task task) {
+        return startBackNavigation(task, null);
+    }
+
+    /**
+     * @param tx, a transaction to be used for the attaching the animation leash.
+     *            This is used in tests. If null, the object will be initialized with a new {@link
+     *            android.view.SurfaceControl.Transaction}
+     * @see #startBackNavigation(Task)
+     */
+    @VisibleForTesting
+    @Nullable
+    BackNavigationInfo startBackNavigation(@NonNull Task task,
+            @Nullable SurfaceControl.Transaction tx) {
+
+        if (tx == null) {
+            tx = new SurfaceControl.Transaction();
+        }
+
+        int backType = BackNavigationInfo.TYPE_UNDEFINED;
+        Task prevTask = task;
+        ActivityRecord prev;
+        WindowContainer<?> removedWindowContainer;
+        ActivityRecord activityRecord;
+        SurfaceControl animationLeashParent;
+        WindowConfiguration taskWindowConfiguration;
+        SurfaceControl animLeash;
+        HardwareBuffer screenshotBuffer = null;
+        int prevTaskId;
+        int prevUserId;
+
+        synchronized (task.mWmService.mGlobalLock) {
+            activityRecord = task.topRunningActivity();
+            removedWindowContainer = activityRecord;
+            taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
+
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, topRunningActivity=%s",
+                    task, activityRecord);
+
+            // IME is visible, back gesture will dismiss it, nothing to preview.
+            if (task.getDisplayContent().getImeContainer().isVisible()) {
+                return null;
+            }
+
+            // Current Activity is home, there is no previous activity to display
+            if (activityRecord.isActivityTypeHome()) {
+                return null;
+            }
+
+            prev = task.getActivity(
+                    (r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity());
+
+            if (prev != null) {
+                backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
+            } else if (task.returnsToHomeRootTask()) {
+                prevTask = null;
+                removedWindowContainer = task;
+                backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+            } else if (activityRecord.isRootOfTask()) {
+                // TODO(208789724): Create single source of truth for this, maybe in
+                //  RootWindowContainer
+                // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic
+                prevTask = task.mRootWindowContainer.getTaskBelow(task);
+                removedWindowContainer = task;
+                if (prevTask.isActivityTypeHome()) {
+                    backType = BackNavigationInfo.TYPE_RETURN_TO_HOME;
+                } else {
+                    prev = prevTask.getTopNonFinishingActivity();
+                    backType = BackNavigationInfo.TYPE_CROSS_TASK;
+                }
+            }
+
+            prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
+            prevUserId = prevTask != null ? prevTask.mUserId : 0;
+
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s",
+                    prev != null ? prev.mActivityComponent : null);
+
+            //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
+            // implemented. For now we simply have the mBackScreenshots hash map that dumbly
+            // saves the screenshots.
+            if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) {
+                screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent);
+            }
+
+            // Prepare a leash to animate the current top window
+            animLeash = removedWindowContainer.makeAnimationLeash()
+                    .setName("BackPreview Leash")
+                    .setHidden(false)
+                    .setBLASTLayer()
+                    .build();
+            removedWindowContainer.reparentSurfaceControl(tx, animLeash);
+
+            animationLeashParent = removedWindowContainer.getAnimationLeashParent();
+        }
+
+        SurfaceControl.Builder builder = new SurfaceControl.Builder()
+                .setName("BackPreview Screenshot")
+                .setParent(animationLeashParent)
+                .setHidden(false)
+                .setBLASTLayer();
+        SurfaceControl screenshotSurface = builder.build();
+
+        // Find a screenshot of the previous activity
+
+        if (needsScreenshot(backType) && prevTask != null) {
+            if (screenshotBuffer == null) {
+                screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
+            }
+        }
+        tx.apply();
+
+        WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
+        try {
+            activityRecord.token.linkToDeath(
+                    () -> resetSurfaces(finalRemovedWindowContainer), 0);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to link to death", e);
+            resetSurfaces(removedWindowContainer);
+            return null;
+        }
+
+        return new BackNavigationInfo(backType,
+                animLeash,
+                screenshotSurface,
+                screenshotBuffer,
+                taskWindowConfiguration,
+                new RemoteCallback(result -> resetSurfaces(finalRemovedWindowContainer
+                )));
+    }
+
+
+    private HardwareBuffer getActivitySnapshot(@NonNull Task task,
+            ComponentName activityComponent) {
+        // Check if we have a screenshot of the previous activity, indexed by its
+        // component name.
+        SurfaceControl.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
+                .get(activityComponent.flattenToString());
+        return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
+
+    }
+
+    private HardwareBuffer getTaskSnapshot(int taskId, int userId) {
+        if (mTaskSnapshotController == null) {
+            return null;
+        }
+        TaskSnapshot snapshot = mTaskSnapshotController.getSnapshot(taskId,
+                userId, true /* restoreFromDisk */, false  /* isLowResolution */);
+        return snapshot != null ? snapshot.getHardwareBuffer() : null;
+    }
+
+    private boolean needsScreenshot(int backType) {
+        switch (backType) {
+            case BackNavigationInfo.TYPE_RETURN_TO_HOME:
+            case BackNavigationInfo.TYPE_DIALOG_CLOSE:
+                return false;
+        }
+        return true;
+    }
+
+    private void resetSurfaces(@NonNull WindowContainer<?> windowContainer) {
+        synchronized (windowContainer.mWmService.mGlobalLock) {
+            ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Back: Reset surfaces");
+            SurfaceControl.Transaction tx = windowContainer.getSyncTransaction();
+            SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
+            if (surfaceControl != null) {
+                tx.reparent(surfaceControl,
+                        windowContainer.getParent().getSurfaceControl());
+                tx.apply();
+            }
+        }
+    }
+
+    void setTaskSnapshotController(@Nullable TaskSnapshotController taskSnapshotController) {
+        mTaskSnapshotController = taskSnapshotController;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index b681a96..c8781ae 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -39,6 +39,7 @@
 import static android.view.WindowManager.TRANSIT_NONE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
 import static com.android.server.wm.ActivityRecord.State.PAUSED;
 import static com.android.server.wm.ActivityRecord.State.PAUSING;
@@ -100,6 +101,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Objects;
 import java.util.function.Consumer;
@@ -254,6 +256,10 @@
     private final Rect mTmpStableBounds = new Rect();
     private final Rect mTmpNonDecorBounds = new Rect();
 
+    //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
+    // implemented
+    HashMap<String, SurfaceControl.ScreenshotHardwareBuffer> mBackScreenshots = new HashMap<>();
+
     private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
             new EnsureActivitiesVisibleHelper(this);
     private final EnsureVisibleActivitiesConfigHelper mEnsureVisibleActivitiesConfigHelper =
@@ -1683,6 +1689,7 @@
 
     @Override
     void addChild(WindowContainer child, int index) {
+        ActivityRecord r = topRunningActivity();
         mClearedTaskForReuse = false;
 
         boolean isAddingActivity = child.asActivityRecord() != null;
@@ -1697,6 +1704,18 @@
         super.addChild(child, index);
 
         if (isAddingActivity && task != null) {
+
+            // TODO(b/207481538): temporary per-activity screenshoting
+            if (r != null && BackNavigationController.isEnabled()) {
+                ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
+                        r.mActivityComponent.flattenToString());
+                Rect outBounds = r.getBounds();
+                SurfaceControl.ScreenshotHardwareBuffer backBuffer = SurfaceControl.captureLayers(
+                        r.mSurfaceControl,
+                        new Rect(0, 0, outBounds.width(), outBounds.height()),
+                        1f);
+                mBackScreenshots.put(r.mActivityComponent.flattenToString(), backBuffer);
+            }
             child.asActivityRecord().inHistory = true;
             task.onDescendantActivityAdded(taskHadActivity, activityType, child.asActivityRecord());
         }
@@ -2290,6 +2309,14 @@
 
     void removeChild(WindowContainer child, boolean removeSelfIfPossible) {
         super.removeChild(child);
+        if (BackNavigationController.isEnabled()) {
+            //TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
+            // implemented
+            ActivityRecord r = child.asActivityRecord();
+            if (r != null) {
+                mBackScreenshots.remove(r.mActivityComponent.flattenToString());
+            }
+        }
         if (removeSelfIfPossible && (!mCreatedByOrganizer || mIsRemovalRequested) && !hasChild()) {
             removeImmediately("removeLastChild " + child);
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 233f2ff..a7b7d1a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -194,7 +194,7 @@
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.tv.TvRemoteService;
-import com.android.server.tv.interactive.TvIAppManagerService;
+import com.android.server.tv.interactive.TvInteractiveAppManagerService;
 import com.android.server.tv.tunerresourcemanager.TunerResourceManagerService;
 import com.android.server.twilight.TwilightService;
 import com.android.server.uri.UriGrantsManagerService;
@@ -2379,8 +2379,8 @@
 
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_LIVE_TV)
                     || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
-                t.traceBegin("StartTvIAppManager");
-                mSystemServiceManager.startService(TvIAppManagerService.class);
+                t.traceBegin("StartTvInteractiveAppManager");
+                mSystemServiceManager.startService(TvInteractiveAppManagerService.class);
                 t.traceEnd();
             }
 
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 715fe6e..6e72479 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -18,9 +18,11 @@
 
 import android.annotation.NonNull;
 import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -33,6 +35,7 @@
 import android.media.midi.IMidiDeviceOpenCallback;
 import android.media.midi.IMidiDeviceServer;
 import android.media.midi.IMidiManager;
+import android.media.midi.MidiDevice;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceService;
 import android.media.midi.MidiDeviceStatus;
@@ -55,6 +58,7 @@
 import org.xmlpull.v1.XmlPullParser;
 
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -96,9 +100,12 @@
             = new HashMap<MidiDeviceInfo, Device>();
 
     // list of all Bluetooth devices, keyed by BluetoothDevice
-     private final HashMap<BluetoothDevice, Device> mBluetoothDevices
+    private final HashMap<BluetoothDevice, Device> mBluetoothDevices
             = new HashMap<BluetoothDevice, Device>();
 
+    private final HashMap<BluetoothDevice, MidiDevice> mBleMidiDeviceMap =
+            new HashMap<BluetoothDevice, MidiDevice>();
+
     // list of all devices, keyed by IMidiDeviceServer
     private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>();
 
@@ -569,10 +576,45 @@
         }
     }
 
+    private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "MidiService, action is null");
+                return;
+            }
+
+            switch (action) {
+                case BluetoothDevice.ACTION_ACL_CONNECTED: {
+                    Log.d(TAG, "ACTION_ACL_CONNECTED");
+                    BluetoothDevice btDevice =
+                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    openBluetoothDevice(btDevice);
+                }
+                break;
+
+                case BluetoothDevice.ACTION_ACL_DISCONNECTED: {
+                    Log.d(TAG, "ACTION_ACL_DISCONNECTED");
+                    BluetoothDevice btDevice =
+                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                    closeBluetoothDevice(btDevice);
+                }
+                break;
+            }
+        }
+    };
+
     public MidiService(Context context) {
         mContext = context;
         mPackageManager = context.getPackageManager();
 
+        // Setup broadcast receivers
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
+        filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        context.registerReceiver(mBleMidiReceiver, filter);
+
         mBluetoothServiceUid = -1;
     }
 
@@ -701,9 +743,43 @@
         }
     }
 
+    private void openBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        Log.d(TAG, "openBluetoothDevice() device: " + bluetoothDevice);
+
+        MidiManager midiManager = mContext.getSystemService(MidiManager.class);
+        midiManager.openBluetoothDevice(bluetoothDevice,
+                new MidiManager.OnDeviceOpenedListener() {
+                    @Override
+                    public void onDeviceOpened(MidiDevice device) {
+                        synchronized (mBleMidiDeviceMap) {
+                            mBleMidiDeviceMap.put(bluetoothDevice, device);
+                        }
+                    }
+                }, null);
+    }
+
+    private void closeBluetoothDevice(BluetoothDevice bluetoothDevice) {
+        Log.d(TAG, "closeBluetoothDevice() device: " + bluetoothDevice);
+
+        MidiDevice midiDevice;
+        synchronized (mBleMidiDeviceMap) {
+            midiDevice = mBleMidiDeviceMap.remove(bluetoothDevice);
+        }
+
+        if (midiDevice != null) {
+            try {
+                midiDevice.close();
+            } catch (IOException ex) {
+                Log.e(TAG, "Exception closing BLE-MIDI device" + ex);
+            }
+        }
+    }
+
     @Override
     public void openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice,
             IMidiDeviceOpenCallback callback) {
+        Log.d(TAG, "openBluetoothDevice()");
+
         Client client = getClient(token);
         if (client == null) return;
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
index a06a782..fc55a9f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java
@@ -52,7 +52,7 @@
     @Mock
     private ClientMonitorCallbackConverter mClientCallback;
     @Mock
-    private BaseClientMonitor.Callback mSchedulerCallback;
+    private ClientMonitorCallback mSchedulerCallback;
 
     @Before
     public void setUp() {
@@ -96,7 +96,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
             startHalOperation();
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
new file mode 100644
index 0000000..51d234d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.biometrics.log.BiometricLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@Presubmit
+@SmallTest
+public class BaseClientMonitorTest {
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private IBinder mToken;
+    private @Mock ClientMonitorCallbackConverter mListener;
+    @Mock
+    private BiometricLogger mLogger;
+    @Mock
+    private ClientMonitorCallback mCallback;
+
+    private TestClientMonitor mClientMonitor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mClientMonitor = new TestClientMonitor();
+    }
+
+    @Test
+    public void preparesForDeath() throws RemoteException {
+        verify(mToken).linkToDeath(eq(mClientMonitor), anyInt());
+
+        mClientMonitor.binderDied();
+
+        assertThat(mClientMonitor.mCanceled).isTrue();
+        assertThat(mClientMonitor.getListener()).isNull();
+    }
+
+    @Test
+    public void ignoresDeathWhenDone() {
+        mClientMonitor.markAlreadyDone();
+        mClientMonitor.binderDied();
+
+        assertThat(mClientMonitor.mCanceled).isFalse();
+    }
+
+    @Test
+    public void start() {
+        mClientMonitor.start(mCallback);
+
+        verify(mCallback).onClientStarted(eq(mClientMonitor));
+    }
+
+    @Test
+    public void destroy() {
+        mClientMonitor.destroy();
+        mClientMonitor.destroy();
+
+        assertThat(mClientMonitor.isAlreadyDone()).isTrue();
+        verify(mToken).unlinkToDeath(eq(mClientMonitor), anyInt());
+    }
+
+    @Test
+    public void hasRequestId() {
+        assertThat(mClientMonitor.hasRequestId()).isFalse();
+
+        final int id = 200;
+        mClientMonitor.setRequestId(id);
+        assertThat(mClientMonitor.hasRequestId()).isTrue();
+        assertThat(mClientMonitor.getRequestId()).isEqualTo(id);
+    }
+
+    private class TestClientMonitor extends BaseClientMonitor implements Interruptable {
+        public boolean mCanceled = false;
+
+        TestClientMonitor() {
+            super(mContext, mToken, mListener, 9 /* userId */, "foo" /* owner */, 2 /* cookie */,
+                    5 /* sensorId */, mLogger);
+        }
+
+        @Override
+        public int getProtoEnum() {
+            return 0;
+        }
+
+        @Override
+        public void cancel() {
+            mCanceled = true;
+        }
+
+        @Override
+        public void cancelWithoutStarting(@NonNull ClientMonitorCallback callback) {
+            mCanceled = true;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index d4bac2c..8751cf3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -61,11 +61,11 @@
     @Mock
     private InterruptableMonitor<FakeHal> mClientMonitor;
     @Mock
-    private BaseClientMonitor.Callback mClientCallback;
+    private ClientMonitorCallback mClientCallback;
     @Mock
     private FakeHal mHal;
     @Captor
-    ArgumentCaptor<BaseClientMonitor.Callback> mStartCallback;
+    ArgumentCaptor<ClientMonitorCallback> mStartCallback;
 
     private Handler mHandler;
     private BiometricSchedulerOperation mOperation;
@@ -89,7 +89,7 @@
         assertThat(mOperation.isFinished()).isFalse();
 
         final boolean started = mOperation.startWithCookie(
-                mock(BaseClientMonitor.Callback.class), cookie);
+                mock(ClientMonitorCallback.class), cookie);
 
         assertThat(started).isTrue();
         verify(mClientMonitor).start(mStartCallback.capture());
@@ -106,7 +106,7 @@
 
         assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie);
         final boolean started = mOperation.startWithCookie(
-                mock(BaseClientMonitor.Callback.class), badCookie);
+                mock(ClientMonitorCallback.class), badCookie);
 
         assertThat(started).isFalse();
         assertThat(mOperation.isStarted()).isFalse();
@@ -119,7 +119,7 @@
         when(mClientMonitor.getCookie()).thenReturn(0);
         when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
-        final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
         mOperation.start(cb);
         verify(mClientMonitor).start(mStartCallback.capture());
         mStartCallback.getValue().onClientStarted(mClientMonitor);
@@ -146,7 +146,7 @@
         when(mClientMonitor.getCookie()).thenReturn(0);
         when(mClientMonitor.getFreshDaemon()).thenReturn(null);
 
-        final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
         mOperation.start(cb);
         verify(mClientMonitor, never()).start(any());
 
@@ -164,17 +164,17 @@
     public void doesNotStartWithCookie() {
         when(mClientMonitor.getCookie()).thenReturn(9);
         assertThrows(IllegalStateException.class,
-                () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+                () -> mOperation.start(mock(ClientMonitorCallback.class)));
     }
 
     @Test
     public void cannotRestart() {
         when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
-        mOperation.start(mock(BaseClientMonitor.Callback.class));
+        mOperation.start(mock(ClientMonitorCallback.class));
 
         assertThrows(IllegalStateException.class,
-                () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+                () -> mOperation.start(mock(ClientMonitorCallback.class)));
     }
 
     @Test
@@ -187,14 +187,14 @@
         verify(mClientMonitor).unableToStart();
         verify(mClientMonitor).destroy();
         assertThrows(IllegalStateException.class,
-                () -> mOperation.start(mock(BaseClientMonitor.Callback.class)));
+                () -> mOperation.start(mock(ClientMonitorCallback.class)));
     }
 
     @Test
     public void cannotAbortRunning() {
         when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
-        mOperation.start(mock(BaseClientMonitor.Callback.class));
+        mOperation.start(mock(ClientMonitorCallback.class));
 
         assertThrows(IllegalStateException.class, () -> mOperation.abort());
     }
@@ -203,8 +203,8 @@
     public void cancel() {
         when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
-        final BaseClientMonitor.Callback startCb = mock(BaseClientMonitor.Callback.class);
-        final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback startCb = mock(ClientMonitorCallback.class);
+        final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
         mOperation.start(startCb);
         verify(mClientMonitor).start(mStartCallback.capture());
         mStartCallback.getValue().onClientStarted(mClientMonitor);
@@ -230,12 +230,12 @@
     public void cancelWithoutStarting() {
         when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
-        final BaseClientMonitor.Callback cancelCb = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class);
         mOperation.cancel(mHandler, cancelCb);
 
         assertThat(mOperation.isCanceling()).isTrue();
-        ArgumentCaptor<BaseClientMonitor.Callback> cbCaptor =
-                ArgumentCaptor.forClass(BaseClientMonitor.Callback.class);
+        ArgumentCaptor<ClientMonitorCallback> cbCaptor =
+                ArgumentCaptor.forClass(ClientMonitorCallback.class);
         verify(mClientMonitor).cancelWithoutStarting(cbCaptor.capture());
 
         cbCaptor.getValue().onClientFinished(mClientMonitor, true);
@@ -278,7 +278,7 @@
         }
 
         mOperation.markCanceling();
-        final BaseClientMonitor.Callback cb = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback cb = mock(ClientMonitorCallback.class);
         if (withCookie != null) {
             mOperation.startWithCookie(cb, withCookie);
         } else {
@@ -307,12 +307,12 @@
     private void cancelWatchdog(boolean start) {
         when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
 
-        mOperation.start(mock(BaseClientMonitor.Callback.class));
+        mOperation.start(mock(ClientMonitorCallback.class));
         if (start) {
             verify(mClientMonitor).start(mStartCallback.capture());
             mStartCallback.getValue().onClientStarted(mClientMonitor);
         }
-        mOperation.cancel(mHandler, mock(BaseClientMonitor.Callback.class));
+        mOperation.cancel(mHandler, mock(ClientMonitorCallback.class));
 
         assertThat(mOperation.isCanceling()).isTrue();
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
index ac08319..c99d656 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java
@@ -114,13 +114,13 @@
         final TestHalClientMonitor client2 = new TestHalClientMonitor(
                 mContext, mToken, () -> mock(Object.class));
 
-        final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
-        final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+        final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
 
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
         mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
-                mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
+                mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
         assertEquals(1, mScheduler.mPendingOperations.size());
@@ -152,13 +152,13 @@
         final TestHalClientMonitor client2 =
                 new TestHalClientMonitor(mContext, mToken, () -> daemon2);
 
-        final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
-        final BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
+        final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class);
 
         // Pretend the scheduler is busy so the first operation doesn't start right away. We want
         // to pretend like there are two operations in the queue before kicking things off
         mScheduler.mCurrentOperation = new BiometricSchedulerOperation(
-                mock(BaseClientMonitor.class), mock(BaseClientMonitor.Callback.class));
+                mock(BaseClientMonitor.class), mock(ClientMonitorCallback.class));
 
         mScheduler.scheduleClientMonitor(client1, callback1);
         assertEquals(1, mScheduler.mPendingOperations.size());
@@ -187,7 +187,7 @@
         final HalClientMonitor.LazyDaemon<Object> lazyDaemon1 = () -> mock(Object.class);
         final TestAuthenticationClient client1 = new TestAuthenticationClient(mContext,
                 lazyDaemon1, mToken, mock(ClientMonitorCallbackConverter.class));
-        final BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
+        final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class);
 
         // Schedule a BiometricPrompt authentication request
         mScheduler.scheduleClientMonitor(client1, callback1);
@@ -628,7 +628,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
             assertFalse(mStarted);
             mStarted = true;
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
index 09b5c5c..587bb60 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java
@@ -17,36 +17,62 @@
 package com.android.server.biometrics.sensors;
 
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
 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.Before;
 import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Objects;
 
 @Presubmit
 @SmallTest
 public class CompositeCallbackTest {
 
+    @Mock
+    private BaseClientMonitor mClientMonitor;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+    }
+
     @Test
-    public void testNullCallback() {
-        BaseClientMonitor.Callback callback1 = mock(BaseClientMonitor.Callback.class);
-        BaseClientMonitor.Callback callback2 = mock(BaseClientMonitor.Callback.class);
-        BaseClientMonitor.Callback callback3 = null;
+    public void testCallbacks() {
+        testCallbacks(mock(ClientMonitorCallback.class), mock(ClientMonitorCallback.class));
+    }
 
-        BaseClientMonitor.CompositeCallback callback = new BaseClientMonitor.CompositeCallback(
-                callback1, callback2, callback3);
+    @Test
+    public void testNullCallbacks() {
+        testCallbacks(null, mock(ClientMonitorCallback.class),
+                null, mock(ClientMonitorCallback.class));
+    }
 
-        BaseClientMonitor clientMonitor = mock(BaseClientMonitor.class);
+    private void testCallbacks(ClientMonitorCallback... callbacks) {
+        final ClientMonitorCallback[] expected = Arrays.stream(callbacks)
+                .filter(Objects::nonNull).toArray(ClientMonitorCallback[]::new);
 
-        callback.onClientStarted(clientMonitor);
-        verify(callback1).onClientStarted(eq(clientMonitor));
-        verify(callback2).onClientStarted(eq(clientMonitor));
+        ClientMonitorCompositeCallback callback = new ClientMonitorCompositeCallback(callbacks);
 
-        callback.onClientFinished(clientMonitor, true /* success */);
-        verify(callback1).onClientFinished(eq(clientMonitor), eq(true));
-        verify(callback2).onClientFinished(eq(clientMonitor), eq(true));
+        callback.onClientStarted(mClientMonitor);
+        final InOrder order = inOrder(expected);
+        for (ClientMonitorCallback cb : expected) {
+            order.verify(cb).onClientStarted(eq(mClientMonitor));
+        }
+
+        callback.onClientFinished(mClientMonitor, true /* success */);
+        Collections.reverse(Arrays.asList(expected));
+        for (ClientMonitorCallback cb : expected) {
+            order.verify(cb).onClientFinished(eq(mClientMonitor), eq(true));
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
index 407f5fb..a11709a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java
@@ -155,7 +155,7 @@
         assertNull(mScheduler.mCurrentOperation);
 
         final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation(
-                mock(BaseClientMonitor.class), new BaseClientMonitor.Callback() {});
+                mock(BaseClientMonitor.class), new ClientMonitorCallback() {});
         mScheduler.mCurrentOperation = fakeOperation;
         startUserClient.mCallback.onClientFinished(startUserClient, true);
         assertSame(fakeOperation, mScheduler.mCurrentOperation);
@@ -234,7 +234,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
             onUserStopped();
         }
@@ -248,7 +248,7 @@
     private static class TestStartUserClient extends StartUserClient<Object, Object> {
         private final boolean mShouldFinish;
 
-        Callback mCallback;
+        ClientMonitorCallback mCallback;
 
         public TestStartUserClient(@NonNull Context context,
                 @NonNull LazyDaemon<Object> lazyDaemon, @Nullable IBinder token, int userId,
@@ -263,7 +263,7 @@
         }
 
         @Override
-        public void start(@NonNull Callback callback) {
+        public void start(@NonNull ClientMonitorCallback callback) {
             super.start(callback);
 
             mCallback = callback;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
index 55dc035..931fad1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java
@@ -34,7 +34,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.SmallTest;
 
-import com.android.server.biometrics.sensors.BaseClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallback;
 import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
 
 import org.junit.Before;
@@ -61,7 +61,7 @@
     @Mock
     private IFaceServiceReceiver mOtherReceiver;
     @Mock
-    private BaseClientMonitor.Callback mMonitorCallback;
+    private ClientMonitorCallback mMonitorCallback;
 
     private FaceGenerateChallengeClient mClient;
 
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index d164d2a..0e98b5e 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -29,6 +29,7 @@
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.argThat;
 import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -38,6 +39,7 @@
 import android.app.StatusBarManager;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.om.IOverlayManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
@@ -96,6 +98,10 @@
     private ApplicationInfo mApplicationInfo;
     @Mock
     private IStatusBar.Stub mMockStatusBar;
+    @Mock
+    private IOverlayManager mOverlayManager;
+    @Mock
+    private PackageManager mPackageManager;
     @Captor
     private ArgumentCaptor<IAddTileResultCallback> mAddTileResultCallbackCaptor;
 
@@ -130,6 +136,7 @@
                 mStatusBarManagerService);
 
         mStatusBarManagerService.registerStatusBar(mMockStatusBar);
+        mStatusBarManagerService.registerOverlayManager(mOverlayManager);
 
         mIcon = Icon.createWithResource(mContext, android.R.drawable.btn_plus);
     }
@@ -577,27 +584,56 @@
     }
 
     @Test
-    public void testSetNavBarModeOverride_setsOverrideModeKids() {
+    public void testSetNavBarModeOverride_setsOverrideModeKids() throws RemoteException {
         int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
         mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
 
         assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager).setEnabledExclusiveInCategory(anyString(), anyInt());
     }
 
     @Test
-    public void testSetNavBarModeOverride_setsOverrideModeNone() {
+    public void testSetNavBarModeOverride_setsOverrideModeNone() throws RemoteException {
         int navBarModeOverrideNone = StatusBarManager.NAV_BAR_MODE_OVERRIDE_NONE;
+
         mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideNone);
 
         assertEquals(navBarModeOverrideNone, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
     }
 
     @Test
-    public void testSetNavBarModeOverride_invalidInputThrowsError() {
+    public void testSetNavBarModeOverride_invalidInputThrowsError() throws RemoteException {
         int navBarModeOverrideInvalid = -1;
 
         assertThrows(UnsupportedOperationException.class,
                 () -> mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideInvalid));
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNavBarModeOverride_noOverlayManagerDoesNotEnable() throws RemoteException {
+        mOverlayManager = null;
+        int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+        mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+        assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
+    }
+
+    @Test
+    public void testSetNavBarModeOverride_noPackageDoesNotEnable() throws Exception {
+        mContext.setMockPackageManager(mPackageManager);
+        when(mPackageManager.getPackageInfo(anyString(),
+                any(PackageManager.PackageInfoFlags.class))).thenReturn(null);
+        int navBarModeOverrideKids = StatusBarManager.NAV_BAR_MODE_OVERRIDE_KIDS;
+
+        mStatusBarManagerService.setNavBarModeOverride(navBarModeOverrideKids);
+
+        assertEquals(navBarModeOverrideKids, mStatusBarManagerService.getNavBarModeOverride());
+        verify(mOverlayManager, never()).setEnabledExclusiveInCategory(anyString(), anyInt());
     }
 
     private void mockUidCheck() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
new file mode 100644
index 0000000..687779d
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -0,0 +1,122 @@
+/*
+ * 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.wm;
+
+import static android.window.BackNavigationInfo.typeToString;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.hardware.HardwareBuffer;
+import android.platform.test.annotations.Presubmit;
+import android.window.BackNavigationInfo;
+import android.window.TaskSnapshot;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class BackNavigationControllerTests extends WindowTestsBase {
+
+    private BackNavigationController mBackNavigationController;
+
+    @Before
+    public void setUp() throws Exception {
+        mBackNavigationController = new BackNavigationController();
+    }
+
+    @Test
+    public void backTypeHomeWhenBackToLauncher() {
+        Task task = createTopTaskWithActivity();
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
+    }
+
+    @Test
+    public void backTypeCrossTaskWhenBackToPreviousTask() {
+        Task taskA = createTask(mDefaultDisplay);
+        createActivityRecord(taskA);
+        Task task = createTopTaskWithActivity();
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_TASK));
+    }
+
+    @Test
+    public void backTypeCrossActivityWhenBackToPreviousActivity() {
+        Task task = createTopTaskWithActivity();
+        mAtm.setFocusedTask(task.mTaskId, createActivityRecord(task));
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(typeToString(backNavigationInfo.getType()))
+                .isEqualTo(typeToString(BackNavigationInfo.TYPE_CROSS_ACTIVITY));
+    }
+
+    /**
+     * Checks that we are able to fill all the field of the {@link BackNavigationInfo} object.
+     */
+    @Test
+    public void backNavInfoFullyPopulated() {
+        Task task = createTopTaskWithActivity();
+        createActivityRecord(task);
+
+        // We need a mock screenshot so
+        TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
+
+        mBackNavigationController.setTaskSnapshotController(taskSnapshotController);
+
+        BackNavigationInfo backNavigationInfo =
+                mBackNavigationController.startBackNavigation(task, new StubTransaction());
+        assertThat(backNavigationInfo).isNotNull();
+        assertThat(backNavigationInfo.getDepartingWindowContainer()).isNotNull();
+        assertThat(backNavigationInfo.getScreenshotSurface()).isNotNull();
+        assertThat(backNavigationInfo.getScreenshotHardwareBuffer()).isNotNull();
+        assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
+    }
+
+    @NonNull
+    private TaskSnapshotController createMockTaskSnapshotController() {
+        TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class);
+        TaskSnapshot taskSnapshot = mock(TaskSnapshot.class);
+        when(taskSnapshot.getHardwareBuffer()).thenReturn(mock(HardwareBuffer.class));
+        when(taskSnapshotController.getSnapshot(anyInt(), anyInt(), anyBoolean(), anyBoolean()))
+                .thenReturn(taskSnapshot);
+        return taskSnapshotController;
+    }
+
+    @NonNull
+    private Task createTopTaskWithActivity() {
+        Task task = createTask(mDefaultDisplay);
+        ActivityRecord record = createActivityRecord(task);
+        when(record.mSurfaceControl.isValid()).thenReturn(true);
+        mAtm.setFocusedTask(task.mTaskId, record);
+        return task;
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 88725a6..0d88a0d 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -61,6 +61,7 @@
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
@@ -70,6 +71,7 @@
 import android.util.Slog;
 import android.util.SparseLongArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
@@ -128,6 +130,12 @@
     private final CopyOnWriteArrayList<Pair<String, StorageStatsAugmenter>>
             mStorageStatsAugmenters = new CopyOnWriteArrayList<>();
 
+    @GuardedBy("mLock")
+    private int
+            mStorageThresholdPercentHigh = StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH;
+
+    private final Object mLock = new Object();
+
     public StorageStatsService(Context context) {
         mContext = Preconditions.checkNotNull(context);
         mAppOps = Preconditions.checkNotNull(context.getSystemService(AppOpsManager.class));
@@ -173,6 +181,19 @@
                 }
             }
         }, prFilter);
+
+        updateConfig();
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                mContext.getMainExecutor(), properties -> updateConfig());
+    }
+
+    private void updateConfig() {
+        synchronized (mLock) {
+            mStorageThresholdPercentHigh = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+                    StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH_KEY,
+                    StorageManager.DEFAULT_STORAGE_THRESHOLD_PERCENT_HIGH);
+        }
     }
 
     private void invalidateMounts() {
@@ -554,7 +575,7 @@
          * By only triggering a re-calculation after the storage has changed sizes, we can avoid
          * recalculating quotas too often. Minimum change delta high and low define the
          * percentage of change we need to see before we recalculate quotas when the device has
-         * enough storage space (more than StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total
+         * enough storage space (more than mStorageThresholdPercentHigh of total
          * free) and in low storage condition respectively.
          */
         private static final long MINIMUM_CHANGE_DELTA_PERCENT_HIGH = 5;
@@ -588,11 +609,15 @@
                     mStats.restat(Environment.getDataDirectory().getAbsolutePath());
                     long bytesDelta = Math.abs(mPreviousBytes - mStats.getAvailableBytes());
                     long bytesDeltaThreshold;
-                    if (mStats.getAvailableBytes() >  mTotalBytes
-                            * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
-                        bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
-                    } else {
-                        bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+                    synchronized (mLock) {
+                        if (mStats.getAvailableBytes() >  mTotalBytes
+                                * mStorageThresholdPercentHigh / 100) {
+                            bytesDeltaThreshold = mTotalBytes
+                                    * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
+                        } else {
+                            bytesDeltaThreshold = mTotalBytes
+                                    * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+                        }
                     }
                     if (bytesDelta > bytesDeltaThreshold) {
                         mPreviousBytes = mStats.getAvailableBytes();
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
new file mode 100644
index 0000000..f7c0a87
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
new file mode 100644
index 0000000..301fafa
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OWNERS
@@ -0,0 +1,2 @@
+# ime
+# Bug component: 34867
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
new file mode 100644
index 0000000..2c414a2
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OWNERS
@@ -0,0 +1,4 @@
+# System UI > ... > Overview (recent apps) > UI
+# Bug template url: https://b.corp.google.com/issues/new?component=807991&template=1390280 = per-file *Overview*
+# window manager > animations/transitions
+# Bug template url: https://b.corp.google.com/issues/new?component=316275&template=1018192
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
new file mode 100644
index 0000000..897fe5d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/OWNERS
@@ -0,0 +1,2 @@
+# System UI > ... > Launcher > Gesture nav
+# Bug component: 565144
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
new file mode 100644
index 0000000..f7c0a87
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/OWNERS
@@ -0,0 +1,2 @@
+# window manager > animations/transitions
+# Bug component: 316275