Merge "[TIAF] unhide extra IApp types and AppLinkInfo APIs"
diff --git a/core/api/current.txt b/core/api/current.txt
index c65960b..a57499b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -7335,6 +7335,7 @@
   public final class UiAutomation {
     method public void adoptShellPermissionIdentity();
     method public void adoptShellPermissionIdentity(@Nullable java.lang.String...);
+    method public boolean clearCache();
     method @Deprecated public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
     method public void dropShellPermissionIdentity();
@@ -9460,10 +9461,10 @@
     method @Nullable public android.content.IntentSender buildAssociationCancellationIntent();
     method @Nullable public android.content.IntentSender buildPermissionTransferUserConsentIntent(int) throws android.companion.DeviceNotAssociatedException;
     method @RequiresPermission(android.Manifest.permission.DELIVER_COMPANION_MESSAGES) public void detachSystemDataTransport(int) throws android.companion.DeviceNotAssociatedException;
-    method public void disableSystemDataSync(int, int);
+    method public void disableSystemDataSyncForTypes(int, int);
     method @Deprecated public void disassociate(@NonNull String);
     method public void disassociate(int);
-    method public void enableSystemDataSync(int, int);
+    method public void enableSystemDataSyncForTypes(int, int);
     method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
     method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
     method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
@@ -27161,6 +27162,10 @@
     field public static final int SIGNAL_STRENGTH_STRONG = 3; // 0x3
     field public static final int SIGNAL_STRENGTH_WEAK = 2; // 0x2
     field public static final long TIME_SHIFT_INVALID_TIME = -9223372036854775808L; // 0x8000000000000000L
+    field public static final int TIME_SHIFT_MODE_AUTO = 4; // 0x4
+    field public static final int TIME_SHIFT_MODE_LOCAL = 2; // 0x2
+    field public static final int TIME_SHIFT_MODE_NETWORK = 3; // 0x3
+    field public static final int TIME_SHIFT_MODE_OFF = 1; // 0x1
     field public static final int TIME_SHIFT_STATUS_AVAILABLE = 3; // 0x3
     field public static final int TIME_SHIFT_STATUS_UNAVAILABLE = 2; // 0x2
     field public static final int TIME_SHIFT_STATUS_UNKNOWN = 0; // 0x0
@@ -27245,11 +27250,14 @@
     method public void notifyAitInfoUpdated(@NonNull android.media.tv.AitInfo);
     method public void notifyAudioPresentationChanged(@NonNull java.util.List<android.media.AudioPresentation>);
     method public void notifyAudioPresentationSelected(int, int);
+    method public void notifyAvailableSpeeds(@NonNull float[]);
     method public void notifyBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse);
     method public void notifyChannelRetuned(android.net.Uri);
     method public void notifyContentAllowed();
     method public void notifyContentBlocked(@NonNull android.media.tv.TvContentRating);
+    method public void notifyCueingMessageAvailability(boolean);
     method public void notifySignalStrength(int);
+    method public void notifyTimeShiftMode(int);
     method public void notifyTimeShiftStatusChanged(int);
     method public void notifyTrackSelected(int, String);
     method public void notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>);
@@ -27284,6 +27292,7 @@
     method public void onTimeShiftPlay(android.net.Uri);
     method public void onTimeShiftResume();
     method public void onTimeShiftSeekTo(long);
+    method public void onTimeShiftSetMode(int);
     method public void onTimeShiftSetPlaybackParams(android.media.PlaybackParams);
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
@@ -27423,6 +27432,7 @@
     method public void timeShiftPlay(String, android.net.Uri);
     method public void timeShiftResume();
     method public void timeShiftSeekTo(long);
+    method public void timeShiftSetMode(int);
     method public void timeShiftSetPlaybackParams(@NonNull android.media.PlaybackParams);
     method public void tune(@NonNull String, android.net.Uri);
     method public void tune(String, android.net.Uri, android.os.Bundle);
@@ -27443,12 +27453,15 @@
     method public void onAitInfoUpdated(@NonNull String, @NonNull android.media.tv.AitInfo);
     method public void onAudioPresentationSelected(@NonNull String, int, int);
     method public void onAudioPresentationsChanged(@NonNull String, @NonNull java.util.List<android.media.AudioPresentation>);
+    method public void onAvailableSpeeds(@NonNull String, @NonNull float[]);
     method public void onChannelRetuned(String, android.net.Uri);
     method public void onConnectionFailed(String);
     method public void onContentAllowed(String);
     method public void onContentBlocked(String, android.media.tv.TvContentRating);
+    method public void onCueingMessageAvailability(@NonNull String, boolean);
     method public void onDisconnected(String);
     method public void onSignalStrengthUpdated(@NonNull String, int);
+    method public void onTimeShiftMode(@NonNull String, int);
     method public void onTimeShiftStatusChanged(String, int);
     method public void onTrackSelected(String, int, String);
     method public void onTracksChanged(String, java.util.List<android.media.tv.TvTrackInfo>);
@@ -27531,7 +27544,11 @@
     field public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = "command_change_channel_quietly";
     field public static final String COMMAND_PARAMETER_KEY_CHANNEL_URI = "command_channel_uri";
     field public static final String COMMAND_PARAMETER_KEY_INPUT_ID = "command_input_id";
+    field public static final String COMMAND_PARAMETER_KEY_PLAYBACK_PARAMS = "command_playback_params";
+    field public static final String COMMAND_PARAMETER_KEY_PROGRAM_URI = "command_program_uri";
     field public static final String COMMAND_PARAMETER_KEY_STOP_MODE = "command_stop_mode";
+    field public static final String COMMAND_PARAMETER_KEY_TIME_POSITION = "command_time_position";
+    field public static final String COMMAND_PARAMETER_KEY_TIME_SHIFT_MODE = "command_time_shift_mode";
     field public static final String COMMAND_PARAMETER_KEY_TRACK_ID = "command_track_id";
     field public static final String COMMAND_PARAMETER_KEY_TRACK_TYPE = "command_track_type";
     field public static final String COMMAND_PARAMETER_KEY_VOLUME = "command_volume";
@@ -27545,6 +27562,12 @@
     field public static final String PLAYBACK_COMMAND_TYPE_TUNE_PREV = "tune_previous";
     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";
+    field public static final String TIME_SHIFT_COMMAND_TYPE_PAUSE = "pause";
+    field public static final String TIME_SHIFT_COMMAND_TYPE_PLAY = "play";
+    field public static final String TIME_SHIFT_COMMAND_TYPE_RESUME = "resume";
+    field public static final String TIME_SHIFT_COMMAND_TYPE_SEEK_TO = "seek_to";
+    field public static final String TIME_SHIFT_COMMAND_TYPE_SET_MODE = "set_mode";
+    field public static final String TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS = "set_playback_params";
   }
 
   public abstract static class TvInteractiveAppService.Session implements android.view.KeyEvent.Callback {
@@ -27557,6 +27580,7 @@
     method @CallSuper public final void notifyTeletextAppStateChanged(int);
     method public void onAdBufferConsumed(@NonNull android.media.tv.AdBuffer);
     method public void onAdResponse(@NonNull android.media.tv.AdResponse);
+    method public void onAvailableSpeeds(@NonNull float[]);
     method public void onBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse);
     method public void onContentAllowed();
     method public void onContentBlocked(@NonNull android.media.tv.TvContentRating);
@@ -27586,6 +27610,11 @@
     method public void onStopInteractiveApp();
     method public void onStreamVolume(float);
     method public void onSurfaceChanged(int, int, int);
+    method public void onTimeShiftCurrentPositionChanged(@NonNull String, long);
+    method public void onTimeShiftMode(int);
+    method public void onTimeShiftPlaybackParams(@NonNull android.media.PlaybackParams);
+    method public void onTimeShiftStartPositionChanged(@NonNull String, long);
+    method public void onTimeShiftStatusChanged(@NonNull String, int);
     method public boolean onTouchEvent(@NonNull android.view.MotionEvent);
     method public void onTrackInfoList(@NonNull java.util.List<android.media.tv.TvTrackInfo>);
     method public void onTrackSelected(int, @NonNull String);
@@ -27599,6 +27628,7 @@
     method public void onVideoUnavailable(int);
     method @CallSuper public void removeBroadcastInfo(int);
     method @CallSuper public void requestAd(@NonNull android.media.tv.AdRequest);
+    method @CallSuper public void requestAvailableSpeeds();
     method @CallSuper public void requestBroadcastInfo(@NonNull android.media.tv.BroadcastInfoRequest);
     method @CallSuper public void requestCurrentChannelLcn();
     method @CallSuper public void requestCurrentChannelUri();
@@ -27608,10 +27638,12 @@
     method @CallSuper public void requestStartRecording(@Nullable android.net.Uri);
     method @CallSuper public void requestStopRecording(@NonNull String);
     method @CallSuper public void requestStreamVolume();
+    method @CallSuper public void requestTimeShiftMode();
     method @CallSuper public void requestTrackInfoList();
     method @CallSuper public void requestTvRecordingInfo(@NonNull String);
     method @CallSuper public void requestTvRecordingInfoList(@NonNull int);
     method @CallSuper public void sendPlaybackCommandRequest(@NonNull String, @Nullable android.os.Bundle);
+    method @CallSuper public void sendTimeShiftCommandRequest(@NonNull String, @Nullable android.os.Bundle);
     method @CallSuper public void setMediaViewEnabled(boolean);
     method @CallSuper public void setTvRecordingInfo(@NonNull String, @NonNull android.media.tv.TvRecordingInfo);
     method @CallSuper public void setVideoBounds(@NonNull android.graphics.Rect);
@@ -27646,6 +27678,10 @@
     method public void notifyError(@NonNull String, @NonNull android.os.Bundle);
     method public void notifyRecordingStarted(@NonNull String);
     method public void notifyRecordingStopped(@NonNull String);
+    method public void notifyTimeShiftCurrentPositionChanged(@NonNull String, long);
+    method public void notifyTimeShiftPlaybackParams(@NonNull android.media.PlaybackParams);
+    method public void notifyTimeShiftStartPositionChanged(@NonNull String, long);
+    method public void notifyTimeShiftStatusChanged(@NonNull String, int);
     method public void notifyTvMessage(@NonNull String, @NonNull android.os.Bundle);
     method public void onAttachedToWindow();
     method public void onDetachedFromWindow();
@@ -27656,12 +27692,14 @@
     method public void prepareInteractiveApp(@NonNull String, int);
     method public void reset();
     method public void resetInteractiveApp();
+    method public void sendAvailableSpeeds(@NonNull float[]);
     method public void sendCurrentChannelLcn(int);
     method public void sendCurrentChannelUri(@Nullable android.net.Uri);
     method public void sendCurrentTvInputId(@Nullable String);
     method public void sendCurrentVideoBounds(@NonNull android.graphics.Rect);
     method public void sendSigningResult(@NonNull String, @NonNull byte[]);
     method public void sendStreamVolume(float);
+    method public void sendTimeShiftMode(int);
     method public void sendTrackInfoList(@Nullable java.util.List<android.media.tv.TvTrackInfo>);
     method public void sendTvRecordingInfo(@Nullable android.media.tv.TvRecordingInfo);
     method public void sendTvRecordingInfoList(@NonNull java.util.List<android.media.tv.TvRecordingInfo>);
@@ -27687,6 +27725,7 @@
     ctor public TvInteractiveAppView.TvInteractiveAppCallback();
     method public void onBiInteractiveAppCreated(@NonNull String, @NonNull android.net.Uri, @Nullable String);
     method public void onPlaybackCommandRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
+    method public void onRequestAvailableSpeeds(@NonNull String);
     method public void onRequestCurrentChannelLcn(@NonNull String);
     method public void onRequestCurrentChannelUri(@NonNull String);
     method public void onRequestCurrentTvInputId(@NonNull String);
@@ -27695,6 +27734,7 @@
     method public void onRequestStartRecording(@NonNull String, @Nullable android.net.Uri);
     method public void onRequestStopRecording(@NonNull String, @NonNull String);
     method public void onRequestStreamVolume(@NonNull String);
+    method public void onRequestTimeShiftMode(@NonNull String);
     method public void onRequestTrackInfoList(@NonNull String);
     method public void onRequestTvRecordingInfo(@NonNull String, @NonNull String);
     method public void onRequestTvRecordingInfoList(@NonNull String, @NonNull int);
@@ -27702,6 +27742,7 @@
     method public void onSetVideoBounds(@NonNull String, @NonNull android.graphics.Rect);
     method public void onStateChanged(@NonNull String, int, int);
     method public void onTeletextAppStateChanged(@NonNull String, int);
+    method public void onTimeShiftCommandRequest(@NonNull String, @NonNull String, @NonNull android.os.Bundle);
   }
 
 }
@@ -28822,24 +28863,15 @@
 
   public final class NfcAdapter {
     method public void disableForegroundDispatch(android.app.Activity);
-    method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
     method public void disableReaderMode(android.app.Activity);
     method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
-    method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
     method @Nullable public android.nfc.NfcAntennaInfo getNfcAntennaInfo();
     method public boolean ignore(android.nfc.Tag, int, android.nfc.NfcAdapter.OnTagRemovedListener, android.os.Handler);
-    method @Deprecated public boolean invokeBeam(android.app.Activity);
     method public boolean isEnabled();
-    method @Deprecated public boolean isNdefPushEnabled();
     method public boolean isSecureNfcEnabled();
     method public boolean isSecureNfcSupported();
-    method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
-    method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
-    method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
-    method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
-    method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
     field public static final String ACTION_ADAPTER_STATE_CHANGED = "android.nfc.action.ADAPTER_STATE_CHANGED";
     field public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
     field @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO) public static final String ACTION_PREFERRED_PAYMENT_CHANGED = "android.nfc.action.PREFERRED_PAYMENT_CHANGED";
@@ -33316,6 +33348,7 @@
     field public static final int LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF = 1; // 0x1
     field public static final int LOCATION_MODE_NO_CHANGE = 0; // 0x0
     field public static final int LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF = 4; // 0x4
+    field public static final int LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST = 2; // 0x2
     field public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1; // 0x1
     field public static final String LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN = "com.android.lowpowerstandby.WAKE_ON_LAN";
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
@@ -41269,6 +41302,31 @@
     method public default void onSegmentResults(@NonNull android.os.Bundle);
   }
 
+  public final class RecognitionPart implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getConfidenceLevel();
+    method @Nullable public String getFormattedText();
+    method @NonNull public String getRawText();
+    method public long getTimestampMillis();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int CONFIDENCE_LEVEL_HIGH = 5; // 0x5
+    field public static final int CONFIDENCE_LEVEL_LOW = 1; // 0x1
+    field public static final int CONFIDENCE_LEVEL_LOW_MEDIUM = 2; // 0x2
+    field public static final int CONFIDENCE_LEVEL_MEDIUM = 3; // 0x3
+    field public static final int CONFIDENCE_LEVEL_MEDIUM_HIGH = 4; // 0x4
+    field public static final int CONFIDENCE_LEVEL_UNKNOWN = 0; // 0x0
+    field @NonNull public static final android.os.Parcelable.Creator<android.speech.RecognitionPart> CREATOR;
+  }
+
+  public static final class RecognitionPart.Builder {
+    ctor public RecognitionPart.Builder(@NonNull String);
+    method @NonNull public android.speech.RecognitionPart build();
+    method @NonNull public android.speech.RecognitionPart.Builder setConfidenceLevel(int);
+    method @NonNull public android.speech.RecognitionPart.Builder setFormattedText(@NonNull String);
+    method @NonNull public android.speech.RecognitionPart.Builder setRawText(@NonNull String);
+    method @NonNull public android.speech.RecognitionPart.Builder setTimestampMillis(long);
+  }
+
   public abstract class RecognitionService extends android.app.Service {
     ctor public RecognitionService();
     method public int getMaxConcurrentSessionsCount();
@@ -41358,6 +41416,8 @@
     field public static final String EXTRA_PARTIAL_RESULTS = "android.speech.extra.PARTIAL_RESULTS";
     field public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
     field public static final String EXTRA_PROMPT = "android.speech.extra.PROMPT";
+    field public static final String EXTRA_REQUEST_WORD_CONFIDENCE = "android.speech.extra.REQUEST_WORD_CONFIDENCE";
+    field public static final String EXTRA_REQUEST_WORD_TIMING = "android.speech.extra.REQUEST_WORD_TIMING";
     field public static final String EXTRA_RESULTS = "android.speech.extra.RESULTS";
     field public static final String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
     field public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
@@ -41417,6 +41477,7 @@
     field public static final int ERROR_SERVER_DISCONNECTED = 11; // 0xb
     field public static final int ERROR_SPEECH_TIMEOUT = 6; // 0x6
     field public static final int ERROR_TOO_MANY_REQUESTS = 10; // 0xa
+    field public static final String RECOGNITION_PARTS = "recognition_parts";
     field public static final String RESULTS_ALTERNATIVES = "results_alternatives";
     field public static final String RESULTS_RECOGNITION = "results_recognition";
   }
@@ -41944,18 +42005,6 @@
     method public android.telecom.CallScreeningService.CallResponse.Builder setSkipNotification(boolean);
   }
 
-  public abstract class CallStreamingService extends android.app.Service {
-    ctor public CallStreamingService();
-    method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
-    method public void onCallStreamingStarted(@NonNull android.telecom.StreamingCall);
-    method public void onCallStreamingStateChanged(int);
-    method public void onCallStreamingStopped();
-    field public static final String SERVICE_INTERFACE = "android.telecom.CallStreamingService";
-    field public static final int STREAMING_FAILED_ALREADY_STREAMING = 1; // 0x1
-    field public static final int STREAMING_FAILED_NO_SENDER = 2; // 0x2
-    field public static final int STREAMING_FAILED_SENDER_BINDING_ERROR = 3; // 0x3
-  }
-
   public abstract class Conference extends android.telecom.Conferenceable {
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
@@ -42628,22 +42677,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StatusHints> CREATOR;
   }
 
-  public final class StreamingCall implements android.os.Parcelable {
-    ctor public StreamingCall(@NonNull android.content.ComponentName, @NonNull String, @NonNull android.net.Uri, @NonNull android.os.Bundle);
-    method public int describeContents();
-    method @NonNull public android.net.Uri getAddress();
-    method @NonNull public android.content.ComponentName getComponentName();
-    method @NonNull public String getDisplayName();
-    method @NonNull public android.os.Bundle getExtras();
-    method public int getState();
-    method public void requestStreamingState(int);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StreamingCall> CREATOR;
-    field public static final int STATE_DISCONNECTED = 3; // 0x3
-    field public static final int STATE_HOLDING = 2; // 0x2
-    field public static final int STATE_STREAMING = 1; // 0x1
-  }
-
   public class TelecomManager {
     method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle);
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
@@ -53960,6 +53993,7 @@
     method public java.util.List<java.lang.String> getAvailableExtraData();
     method @Deprecated public void getBoundsInParent(android.graphics.Rect);
     method public void getBoundsInScreen(android.graphics.Rect);
+    method public void getBoundsInWindow(@NonNull android.graphics.Rect);
     method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
     method @Nullable public android.view.accessibility.AccessibilityNodeInfo getChild(int, int);
     method public int getChildCount();
@@ -54038,6 +54072,7 @@
     method public void setAvailableExtraData(java.util.List<java.lang.String>);
     method @Deprecated public void setBoundsInParent(android.graphics.Rect);
     method public void setBoundsInScreen(android.graphics.Rect);
+    method public void setBoundsInWindow(@NonNull android.graphics.Rect);
     method public void setCanOpenPopup(boolean);
     method public void setCheckable(boolean);
     method public void setChecked(boolean);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 5171027..5406333 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -462,6 +462,25 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public static java.util.Map<java.lang.String,java.util.List<android.content.ContentValues>> queryRawContactEntity(@NonNull android.content.ContentResolver, long);
   }
 
+  public class DeviceConfigInitializer {
+    method @Nullable public static android.provider.DeviceConfigServiceManager getDeviceConfigServiceManager();
+    method public static void setDeviceConfigServiceManager(@NonNull android.provider.DeviceConfigServiceManager);
+  }
+
+  public class DeviceConfigServiceManager {
+    method @NonNull public android.provider.DeviceConfigServiceManager.ServiceRegisterer getDeviceConfigUpdatableServiceRegisterer();
+  }
+
+  public static class DeviceConfigServiceManager.ServiceNotFoundException extends java.lang.Exception {
+  }
+
+  public static final class DeviceConfigServiceManager.ServiceRegisterer {
+    method @Nullable public android.os.IBinder get();
+    method @NonNull public android.os.IBinder getOrThrow() throws android.provider.DeviceConfigServiceManager.ServiceNotFoundException;
+    method public void register(@NonNull android.os.IBinder);
+    method @Nullable public android.os.IBinder tryGet();
+  }
+
   public final class Settings {
     field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1
     field public static final int RESET_MODE_TRUSTED_DEFAULTS = 4; // 0x4
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 1fa1e89..8b3696a 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -252,6 +252,22 @@
 
 }
 
+package android.nfc {
+
+  public final class NfcAdapter {
+    method @Deprecated public void disableForegroundNdefPush(android.app.Activity);
+    method @Deprecated public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
+    method @Deprecated public boolean invokeBeam(android.app.Activity);
+    method @Deprecated public boolean isNdefPushEnabled();
+    method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity);
+    method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
+    method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
+    method @Deprecated public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...);
+    method @Deprecated public void setOnNdefPushCompleteCallback(android.nfc.NfcAdapter.OnNdefPushCompleteCallback, android.app.Activity, android.app.Activity...);
+  }
+
+}
+
 package android.os {
 
   public class BatteryManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fde95bd..57759c6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -46,6 +46,7 @@
     field public static final String BIND_ATTENTION_SERVICE = "android.permission.BIND_ATTENTION_SERVICE";
     field public static final String BIND_AUGMENTED_AUTOFILL_SERVICE = "android.permission.BIND_AUGMENTED_AUTOFILL_SERVICE";
     field public static final String BIND_CALL_DIAGNOSTIC_SERVICE = "android.permission.BIND_CALL_DIAGNOSTIC_SERVICE";
+    field public static final String BIND_CALL_STREAMING_SERVICE = "android.permission.BIND_CALL_STREAMING_SERVICE";
     field public static final String BIND_CELL_BROADCAST_SERVICE = "android.permission.BIND_CELL_BROADCAST_SERVICE";
     field @Deprecated public static final String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
     field public static final String BIND_CONTENT_CAPTURE_SERVICE = "android.permission.BIND_CONTENT_CAPTURE_SERVICE";
@@ -174,6 +175,7 @@
     field public static final String MANAGE_CONTENT_CAPTURE = "android.permission.MANAGE_CONTENT_CAPTURE";
     field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
     field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
+    field public static final String MANAGE_DEFAULT_APPLICATIONS = "android.permission.MANAGE_DEFAULT_APPLICATIONS";
     field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
     field public static final String MANAGE_DEVICE_POLICY_APP_EXEMPTIONS = "android.permission.MANAGE_DEVICE_POLICY_APP_EXEMPTIONS";
     field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
@@ -626,6 +628,7 @@
     field public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
     field public static final String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
     field public static final String OPSTR_START_FOREGROUND = "android:start_foreground";
+    field public static final String OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION = "android:system_exempt_from_hibernation";
     field public static final String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
     field public static final String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
     field public static final String OPSTR_TOAST_WINDOW = "android:toast_window";
@@ -1286,6 +1289,7 @@
     field public static final int EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION = 2; // 0x2
     field public static final int EXEMPT_FROM_APP_STANDBY = 0; // 0x0
     field public static final int EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS = 1; // 0x1
+    field public static final int EXEMPT_FROM_HIBERNATION = 3; // 0x3
     field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
     field public static final String EXTRA_LOST_MODE_LOCATION = "android.app.extra.LOST_MODE_LOCATION";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -3185,7 +3189,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.input.VirtualTouchscreenConfig);
     method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
     method public int getDeviceId();
-    method @Nullable public android.companion.virtual.sensor.VirtualSensor getVirtualSensor(int, @NonNull String);
+    method @NonNull public java.util.List<android.companion.virtual.sensor.VirtualSensor> getVirtualSensorList();
     method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull android.content.IntentFilter, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
     method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
@@ -3240,6 +3244,7 @@
     method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setName(@NonNull String);
     method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
+    method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setVirtualSensorCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensorCallback);
   }
 
 }
@@ -3291,14 +3296,18 @@
 
 package android.companion.virtual.sensor {
 
-  public class VirtualSensor {
+  public final class VirtualSensor implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getDeviceId();
     method @NonNull public String getName();
     method public int getType();
     method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendEvent(@NonNull android.companion.virtual.sensor.VirtualSensorEvent);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.sensor.VirtualSensor> CREATOR;
   }
 
-  public static interface VirtualSensor.SensorStateChangeCallback {
-    method public void onStateChanged(boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
+  public interface VirtualSensorCallback {
+    method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration);
   }
 
   public final class VirtualSensorConfig implements android.os.Parcelable {
@@ -3313,7 +3322,6 @@
   public static final class VirtualSensorConfig.Builder {
     ctor public VirtualSensorConfig.Builder(int, @NonNull String);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build();
-    method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setStateChangeCallback(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.sensor.VirtualSensor.SensorStateChangeCallback);
     method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String);
   }
 
@@ -5960,7 +5968,7 @@
     method public boolean isHotPlugDetectActive();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.hardware.usb.DisplayPortAltModeInfo> CREATOR;
-    field public static final int DISPLAYPORT_ALT_MODE_STATUS_CAPABLE = 2; // 0x2
+    field public static final int DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED = 2; // 0x2
     field public static final int DISPLAYPORT_ALT_MODE_STATUS_ENABLED = 3; // 0x3
     field public static final int DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE = 1; // 0x1
     field public static final int DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN = 0; // 0x0
@@ -5978,7 +5986,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USB) public java.util.List<android.hardware.usb.UsbPort> getPorts();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void grantPermission(android.hardware.usb.UsbDevice, String);
     method public static boolean isUvcSupportEnabled();
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public boolean registerDisplayPortAltModeInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.usb.UsbManager.DisplayPortAltModeInfoListener);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void registerDisplayPortAltModeInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.usb.UsbManager.DisplayPortAltModeInfoListener);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void resetUsbGadget();
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
     method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void unregisterDisplayPortAltModeInfoListener(@NonNull android.hardware.usb.UsbManager.DisplayPortAltModeInfoListener);
@@ -9831,11 +9839,15 @@
     method public int getMin5gRssiDbm();
     method public int getMin6gRssiDbm();
     method @NonNull public java.util.List<android.net.wifi.nl80211.PnoNetwork> getPnoNetworks();
+    method public int getScanIntervalMultiplier();
+    method public int getScanIterations();
     method public void setIntervalMillis(long);
     method public void setMin2gRssiDbm(int);
     method public void setMin5gRssiDbm(int);
     method public void setMin6gRssiDbm(int);
     method public void setPnoNetworks(@NonNull java.util.List<android.net.wifi.nl80211.PnoNetwork>);
+    method public void setScanIntervalMultiplier(int);
+    method public void setScanIterations(int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.nl80211.PnoSettings> CREATOR;
   }
@@ -10114,9 +10126,7 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public java.util.Map<java.lang.String,java.lang.Boolean> getTagIntentAppPreferenceForUser(int);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean isControllerAlwaysOn();
@@ -10125,10 +10135,8 @@
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void registerControllerAlwaysOnListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public boolean setControllerAlwaysOn(boolean);
-    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public int setTagIntentAppPreferenceForUser(int, @NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON) public void unregisterControllerAlwaysOnListener(@NonNull android.nfc.NfcAdapter.ControllerAlwaysOnListener);
-    field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
     field public static final int TAG_INTENT_APP_PREF_RESULT_PACKAGE_NOT_FOUND = -1; // 0xffffffff
     field public static final int TAG_INTENT_APP_PREF_RESULT_SUCCESS = 0; // 0x0
     field public static final int TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE = -2; // 0xfffffffe
@@ -12448,7 +12456,7 @@
     method public final void adjustNotification(@NonNull android.service.notification.Adjustment);
     method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>);
     method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int);
-    method public void onAllowedAdjustmentsChanged();
+    method @Deprecated public void onAllowedAdjustmentsChanged();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method public void onNotificationClicked(@NonNull String);
     method public void onNotificationDirectReplied(@NonNull String);
@@ -13301,6 +13309,19 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean);
   }
 
+  public abstract class CallStreamingService extends android.app.Service {
+    ctor public CallStreamingService();
+    method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
+    method public void onCallStreamingStarted(@NonNull android.telecom.StreamingCall);
+    method public void onCallStreamingStateChanged(int);
+    method public void onCallStreamingStopped();
+    field public static final String SERVICE_INTERFACE = "android.telecom.CallStreamingService";
+    field public static final int STREAMING_FAILED_ALREADY_STREAMING = 1; // 0x1
+    field public static final int STREAMING_FAILED_NO_SENDER = 2; // 0x2
+    field public static final int STREAMING_FAILED_SENDER_BINDING_ERROR = 3; // 0x3
+    field public static final int STREAMING_FAILED_UNKNOWN = 0; // 0x0
+  }
+
   public abstract class Conference extends android.telecom.Conferenceable {
     method @Deprecated public final android.telecom.AudioState getAudioState();
     method @Deprecated public final long getConnectTimeMillis();
@@ -13524,6 +13545,22 @@
     method @Deprecated public android.content.ComponentName getPackageName();
   }
 
+  public final class StreamingCall implements android.os.Parcelable {
+    ctor public StreamingCall(@NonNull android.content.ComponentName, @NonNull CharSequence, @NonNull android.net.Uri, @NonNull android.os.Bundle);
+    method public int describeContents();
+    method @NonNull public android.net.Uri getAddress();
+    method @NonNull public android.content.ComponentName getComponentName();
+    method @NonNull public CharSequence getDisplayName();
+    method @NonNull public android.os.Bundle getExtras();
+    method public int getState();
+    method public void requestStreamingState(int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telecom.StreamingCall> CREATOR;
+    field public static final int STATE_DISCONNECTED = 3; // 0x3
+    field public static final int STATE_HOLDING = 2; // 0x2
+    field public static final int STATE_STREAMING = 1; // 0x1
+  }
+
   public final class TelecomAnalytics implements android.os.Parcelable {
     ctor public TelecomAnalytics(java.util.List<android.telecom.TelecomAnalytics.SessionTiming>, java.util.List<android.telecom.ParcelableCallAnalytics>);
     method public int describeContents();
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 2c5acf1..1c10356 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -140,6 +140,17 @@
 
 }
 
+package android.nfc {
+
+  public final class NfcAdapter {
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush();
+    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush();
+    method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int);
+    field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1
+  }
+
+}
+
 package android.os {
 
   public class Build {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1abdbb9..f310d8d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -349,9 +349,7 @@
   }
 
   public class NotificationManager {
-    method public void allowAssistantAdjustment(String);
     method public void cleanUpCallersAfter(long);
-    method public void disallowAssistantAdjustment(String);
     method public android.content.ComponentName getEffectsSuppressor();
     method public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean, boolean);
@@ -448,6 +446,7 @@
     method @Deprecated public boolean grantRuntimePermission(String, String, android.os.UserHandle);
     method public boolean injectInputEvent(@NonNull android.view.InputEvent, boolean, boolean);
     method public void injectInputEventToInputFilter(@NonNull android.view.InputEvent);
+    method public boolean isNodeInCache(@NonNull android.view.accessibility.AccessibilityNodeInfo);
     method @Deprecated public boolean revokeRuntimePermission(String, String, android.os.UserHandle);
     method public void syncInputTransactions();
     method public void syncInputTransactions(boolean);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f78f452..543c5e7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -151,6 +151,8 @@
 import android.provider.CalendarContract;
 import android.provider.CallLog;
 import android.provider.ContactsContract;
+import android.provider.DeviceConfigInitializer;
+import android.provider.DeviceConfigServiceManager;
 import android.provider.Downloads;
 import android.provider.FontsContract;
 import android.provider.Settings;
@@ -8139,8 +8141,11 @@
         MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager());
         BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager());
         BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> {
-            BinderCallsStats.startForBluetooth(context); });
+            BinderCallsStats.startForBluetooth(context);
+        });
         NfcFrameworkInitializer.setNfcServiceManager(new NfcServiceManager());
+
+        DeviceConfigInitializer.setDeviceConfigServiceManager(new DeviceConfigServiceManager());
     }
 
     private void purgePendingResources() {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8263a4e3..8a671be 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1462,9 +1462,17 @@
      */
     public static final int OP_USE_FULL_SCREEN_INTENT = AppProtoEnums.APP_OP_USE_FULL_SCREEN_INTENT;
 
+    /**
+     * Prevent an app from being placed into hibernation.
+     *
+     * @hide
+     */
+    public static final int OP_SYSTEM_EXEMPT_FROM_HIBERNATION =
+            AppProtoEnums.APP_OP_SYSTEM_EXEMPT_FROM_HIBERNATION;
+
     /** @hide */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    public static final int _NUM_OP = 134;
+    public static final int _NUM_OP = 135;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -2058,6 +2066,15 @@
      */
     public static final String OPSTR_USE_FULL_SCREEN_INTENT = "android:use_full_screen_intent";
 
+    /**
+     *  Prevent an app from being placed into hibernation.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION =
+            "android:system_exempt_from_hibernation";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -2580,7 +2597,10 @@
                 .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
         new AppOpInfo.Builder(OP_USE_FULL_SCREEN_INTENT, OPSTR_USE_FULL_SCREEN_INTENT,
                 "USE_FULL_SCREEN_INTENT").setPermission(Manifest.permission.USE_FULL_SCREEN_INTENT)
-                .build()
+                .build(),
+        new AppOpInfo.Builder(OP_SYSTEM_EXEMPT_FROM_HIBERNATION,
+                OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION,
+                "SYSTEM_EXEMPT_FROM_HIBERNATION").build()
     };
 
     // The number of longs needed to form a full bitmask of app ops
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index e93ce6b..c628ec4 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -734,7 +734,22 @@
      * guarantees that the format is stable across devices or Android releases.</p>
      */
     public @Nullable String getDescription() {
-        return mDescription;
+        final StringBuilder sb = new StringBuilder();
+
+        if (mSubReason != SUBREASON_UNKNOWN) {
+            sb.append("[");
+            sb.append(subreasonToString(mSubReason));
+            sb.append("]");
+        }
+
+        if (!TextUtils.isEmpty(mDescription)) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append(mDescription);
+        }
+
+        return sb.toString();
     }
 
     /**
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 302d146..ab32f4d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -80,8 +80,6 @@
     boolean isImportanceLocked(String pkg, int uid);
 
     List<String> getAllowedAssistantAdjustments(String pkg);
-    void allowAssistantAdjustment(String adjustmentType);
-    void disallowAssistantAdjustment(String adjustmentType);
 
     boolean shouldHideSilentStatusIcons(String callingPkg);
     void setHideSilentStatusIcons(boolean hide);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9974e29..7ab65b1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4624,16 +4624,15 @@
         /**
          * Set whether this is an "ongoing" notification.
          *
-
-         * Ongoing notifications cannot be dismissed by the user, so your application or service
-         * must take care of canceling them.
+         * Ongoing notifications cannot be dismissed by the user on locked devices, or by
+         * notification listeners, and some notifications cannnot be dismissed on unlocked 
+         * devices (system, device management, media), so your application or service must take 
+         * care of canceling them.
          *
-
          * They are typically used to indicate a background task that the user is actively engaged
          * with (e.g., playing music) or is pending in some way and therefore occupying the device
          * (e.g., a file download, sync operation, active network connection).
          *
-
          * @see Notification#FLAG_ONGOING_EVENT
          */
         @NonNull
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 4e94a1d..82adaaf 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1577,32 +1577,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    @TestApi
-    public void allowAssistantAdjustment(String capability) {
-        INotificationManager service = getService();
-        try {
-            service.allowAssistantAdjustment(capability);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @TestApi
-    public void disallowAssistantAdjustment(String capability) {
-        INotificationManager service = getService();
-        try {
-            service.disallowAssistantAdjustment(capability);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     /** @hide */
     @TestApi
     public boolean isNotificationPolicyAccessGrantedForPackage(@NonNull String pkg) {
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 249937a..b3b1cf8 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.accessibilityservice.AccessibilityGestureEvent;
+import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityService.Callbacks;
 import android.accessibilityservice.AccessibilityService.IAccessibilityServiceClientWrapper;
 import android.accessibilityservice.AccessibilityServiceInfo;
@@ -58,6 +59,7 @@
 import android.view.Window;
 import android.view.WindowAnimationFrameStats;
 import android.view.WindowContentFrameStats;
+import android.view.accessibility.AccessibilityCache;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -465,6 +467,48 @@
     }
 
     /**
+     * Clears the accessibility cache.
+     *
+     * @return {@code true} if the cache was cleared
+     * @see AccessibilityService#clearCache()
+     */
+    public boolean clearCache() {
+        final int connectionId;
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+            connectionId = mConnectionId;
+        }
+        final AccessibilityCache cache = AccessibilityInteractionClient.getCache(connectionId);
+        if (cache == null) {
+            return false;
+        }
+        cache.clear();
+        return true;
+    }
+
+    /**
+     * Checks if {@code node} is in the accessibility cache.
+     *
+     * @param node the node to check.
+     * @return {@code true} if {@code node} is in the cache.
+     * @hide
+     * @see AccessibilityService#isNodeInCache(AccessibilityNodeInfo)
+     */
+    @TestApi
+    public boolean isNodeInCache(@NonNull AccessibilityNodeInfo node) {
+        final int connectionId;
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+            connectionId = mConnectionId;
+        }
+        final AccessibilityCache cache = AccessibilityInteractionClient.getCache(connectionId);
+        if (cache == null) {
+            return false;
+        }
+        return cache.isNodeInCache(node);
+    }
+
+    /**
      * Adopt the permission identity of the shell UID for all permissions. This allows
      * you to call APIs protected permissions which normal apps cannot hold but are
      * granted to the shell UID. If you already adopted all shell permissions by calling
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e7f6990..409289c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3896,6 +3896,14 @@
     public static final int EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION = 2;
 
     /**
+     * Prevent an app from entering hibernation.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EXEMPT_FROM_HIBERNATION =  3;
+
+    /**
      * Exemptions to platform restrictions, given to an application through
      * {@link #setApplicationExemptions(String, Set)}.
      *
@@ -3904,7 +3912,8 @@
     @IntDef(prefix = { "EXEMPT_FROM_"}, value = {
             EXEMPT_FROM_APP_STANDBY,
             EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
-            EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION
+            EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
+            EXEMPT_FROM_HIBERNATION
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationExemptionConstants {}
@@ -4020,26 +4029,26 @@
         return MTE_NOT_CONTROLLED_BY_POLICY;
     }
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
     public static final String AUTO_TIMEZONE_POLICY = "autoTimezone";
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
     public static final String PERMISSION_GRANT_POLICY = "permissionGrant";
 
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
     public static final String LOCK_TASK_POLICY = "lockTask";
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
@@ -4047,25 +4056,31 @@
             "userControlDisabledPackages";
 
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
     public static final String PERSISTENT_PREFERRED_ACTIVITY_POLICY =
             "persistentPreferredActivity";
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
     public static final String PACKAGE_UNINSTALL_BLOCKED_POLICY = "packageUninstallBlocked";
 
-    // TODO: Expose this as SystemAPI once we add the query API
+    // TODO: Expose this as a public API
     /**
      * @hide
      */
     public static final String APPLICATION_RESTRICTIONS_POLICY = "applicationRestrictions";
 
+    // TODO: Expose this as a public API
+    /**
+     * @hide
+     */
+    public static final String RESET_PASSWORD_TOKEN_POLICY = "resetPasswordToken";
+
     /**
      * This object is a single place to tack on invalidation and disable calls.  All
      * binder caches in this class derive from this Config, so all can be invalidated or
diff --git a/core/java/android/app/admin/LongPolicyValue.java b/core/java/android/app/admin/LongPolicyValue.java
new file mode 100644
index 0000000..b149b8a
--- /dev/null
+++ b/core/java/android/app/admin/LongPolicyValue.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 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.app.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class LongPolicyValue extends PolicyValue<Long> {
+
+    public LongPolicyValue(long value) {
+        super(value);
+    }
+
+    private LongPolicyValue(Parcel source) {
+        this(source.readLong());
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        LongPolicyValue other = (LongPolicyValue) o;
+        return Objects.equals(getValue(), other.getValue());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getValue());
+    }
+
+    @Override
+    public String toString() {
+        return "LongPolicyValue { mValue= " + getValue() + " }";
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(getValue());
+    }
+
+    @NonNull
+    public static final Creator<LongPolicyValue> CREATOR =
+            new Creator<LongPolicyValue>() {
+                @Override
+                public LongPolicyValue createFromParcel(Parcel source) {
+                    return new LongPolicyValue(source);
+                }
+
+                @Override
+                public LongPolicyValue[] newArray(int size) {
+                    return new LongPolicyValue[size];
+                }
+            };
+}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 8842955..5df2d5e 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -491,7 +491,7 @@
      * @param associationId id of the device association.
      * @param flags system data types to be enabled.
      */
-    public void enableSystemDataSync(int associationId, @DataSyncTypes int flags) {
+    public void enableSystemDataSyncForTypes(int associationId, @DataSyncTypes int flags) {
         if (!checkFeaturePresent()) {
             return;
         }
@@ -513,7 +513,7 @@
      * @param associationId id of the device association.
      * @param flags system data types to be disabled.
      */
-    public void disableSystemDataSync(int associationId, @DataSyncTypes int flags) {
+    public void disableSystemDataSyncForTypes(int associationId, @DataSyncTypes int flags) {
         if (!checkFeaturePresent()) {
             return;
         }
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 9ab7cf9..12882a2 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -20,7 +20,7 @@
 import android.companion.virtual.IVirtualDeviceIntentInterceptor;
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
-import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback;
+import android.companion.virtual.sensor.VirtualSensor;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.content.IntentFilter;
@@ -112,16 +112,10 @@
     boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
 
     /**
-     * Creates a virtual sensor, capable of injecting sensor events into the system.
+     * Returns all virtual sensors for this device.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualSensor(IBinder tokenm, in VirtualSensorConfig config);
-
-    /**
-     * Removes the sensor corresponding to the given token from the system.
-     */
-    @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void unregisterSensor(IBinder token);
+    List<VirtualSensor> getVirtualSensorList();
 
     /**
      * Sends an event to the virtual sensor corresponding to the given token.
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 3e6b380..ae43c6e 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -35,7 +35,6 @@
 import android.companion.virtual.camera.VirtualCameraDevice;
 import android.companion.virtual.camera.VirtualCameraInput;
 import android.companion.virtual.sensor.VirtualSensor;
-import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -428,8 +427,6 @@
                 };
         @Nullable
         private VirtualCameraDevice mVirtualCameraDevice;
-        @NonNull
-        private final List<VirtualSensor> mVirtualSensors = new ArrayList<>();
         @Nullable
         private VirtualAudioDevice mVirtualAudioDevice;
 
@@ -448,10 +445,6 @@
                     params,
                     mActivityListenerBinder,
                     mSoundEffectListener);
-            final List<VirtualSensorConfig> virtualSensorConfigs = params.getVirtualSensorConfigs();
-            for (int i = 0; i < virtualSensorConfigs.size(); ++i) {
-                mVirtualSensors.add(createVirtualSensor(virtualSensorConfigs.get(i)));
-            }
         }
 
         /**
@@ -478,20 +471,19 @@
         }
 
         /**
-         * Returns this device's sensor with the given type and name, if any.
+         * Returns this device's sensors.
          *
          * @see VirtualDeviceParams.Builder#addVirtualSensorConfig
          *
-         * @param type The type of the sensor.
-         * @param name The name of the sensor.
-         * @return The matching sensor if found, {@code null} otherwise.
+         * @return A list of all sensors for this device, or an empty list if no sensors exist.
          */
-        @Nullable
-        public VirtualSensor getVirtualSensor(int type, @NonNull String name) {
-            return mVirtualSensors.stream()
-                    .filter(sensor -> sensor.getType() == type && sensor.getName().equals(name))
-                    .findAny()
-                    .orElse(null);
+        @NonNull
+        public List<VirtualSensor> getVirtualSensorList() {
+            try {
+                return mVirtualDevice.getVirtualSensorList();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
 
         /**
@@ -630,9 +622,6 @@
                 @NonNull VirtualDisplayConfig config,
                 @Nullable @CallbackExecutor Executor executor,
                 @Nullable VirtualDisplay.Callback callback) {
-            // TODO(b/205343547): Handle display groups properly instead of creating a new display
-            //  group for every new virtual display created using this API.
-            // belongs to the same display group.
             IVirtualDisplayCallback callbackWrapper =
                     new DisplayManagerGlobal.VirtualDisplayCallback(callback, executor);
             final int displayId;
@@ -941,28 +930,6 @@
         }
 
         /**
-         * Creates a virtual sensor, capable of injecting sensor events into the system. Only for
-         * internal use, since device sensors must remain valid for the entire lifetime of the
-         * device.
-         *
-         * @param config The configuration of the sensor.
-         * @hide
-         */
-        @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-        @NonNull
-        public VirtualSensor createVirtualSensor(@NonNull VirtualSensorConfig config) {
-            Objects.requireNonNull(config);
-            try {
-                final IBinder token = new Binder(
-                        "android.hardware.sensor.VirtualSensor:" + config.getName());
-                mVirtualDevice.createVirtualSensor(token, config);
-                return new VirtualSensor(config.getType(), config.getName(), mVirtualDevice, token);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-
-        /**
          * Adds an activity listener to listen for events such as top activity change or virtual
          * display task stack became empty.
          *
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index d4a0a08..d8076b5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -19,11 +19,18 @@
 import static android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY;
 import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
 
+import static java.util.concurrent.TimeUnit.MICROSECONDS;
+
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
+import android.companion.virtual.sensor.IVirtualSensorCallback;
+import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.content.ComponentName;
 import android.os.Parcel;
@@ -37,11 +44,13 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * Params that can be configured when creating virtual devices.
@@ -190,6 +199,7 @@
     // Mapping of @PolicyType to @DevicePolicy
     @NonNull private final SparseIntArray mDevicePolicies;
     @NonNull private final List<VirtualSensorConfig> mVirtualSensorConfigs;
+    @Nullable private final IVirtualSensorCallback mVirtualSensorCallback;
     @RecentsPolicy
     private final int mDefaultRecentsPolicy;
     private final int mAudioPlaybackSessionId;
@@ -207,6 +217,7 @@
             @Nullable String name,
             @NonNull SparseIntArray devicePolicies,
             @NonNull List<VirtualSensorConfig> virtualSensorConfigs,
+            @Nullable IVirtualSensorCallback virtualSensorCallback,
             @RecentsPolicy int defaultRecentsPolicy,
             int audioPlaybackSessionId,
             int audioRecordingSessionId) {
@@ -224,6 +235,7 @@
         mName = name;
         mDevicePolicies = Objects.requireNonNull(devicePolicies);
         mVirtualSensorConfigs = Objects.requireNonNull(virtualSensorConfigs);
+        mVirtualSensorCallback = virtualSensorCallback;
         mDefaultRecentsPolicy = defaultRecentsPolicy;
         mAudioPlaybackSessionId = audioPlaybackSessionId;
         mAudioRecordingSessionId = audioRecordingSessionId;
@@ -244,6 +256,8 @@
         mDevicePolicies = parcel.readSparseIntArray();
         mVirtualSensorConfigs = new ArrayList<>();
         parcel.readTypedList(mVirtualSensorConfigs, VirtualSensorConfig.CREATOR);
+        mVirtualSensorCallback =
+                IVirtualSensorCallback.Stub.asInterface(parcel.readStrongBinder());
         mDefaultRecentsPolicy = parcel.readInt();
         mAudioPlaybackSessionId = parcel.readInt();
         mAudioRecordingSessionId = parcel.readInt();
@@ -372,6 +386,15 @@
     }
 
     /**
+     * Returns the callback to get notified about changes in the sensor listeners.
+     * @hide
+     */
+    @Nullable
+    public IVirtualSensorCallback getVirtualSensorCallback() {
+        return mVirtualSensorCallback;
+    }
+
+    /**
      * Returns the policy of how to handle activities in recents.
      *
      * @see RecentsPolicy
@@ -417,6 +440,8 @@
         dest.writeString8(mName);
         dest.writeSparseIntArray(mDevicePolicies);
         dest.writeTypedList(mVirtualSensorConfigs);
+        dest.writeStrongBinder(
+                mVirtualSensorCallback != null ? mVirtualSensorCallback.asBinder() : null);
         dest.writeInt(mDefaultRecentsPolicy);
         dest.writeInt(mAudioPlaybackSessionId);
         dest.writeInt(mAudioRecordingSessionId);
@@ -522,11 +547,38 @@
         private boolean mDefaultActivityPolicyConfigured = false;
         @Nullable private String mName;
         @NonNull private SparseIntArray mDevicePolicies = new SparseIntArray();
-        @NonNull private List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>();
         private int mDefaultRecentsPolicy;
         private int mAudioPlaybackSessionId = AUDIO_SESSION_ID_GENERATE;
         private int mAudioRecordingSessionId = AUDIO_SESSION_ID_GENERATE;
 
+        @NonNull private List<VirtualSensorConfig> mVirtualSensorConfigs = new ArrayList<>();
+        @Nullable
+        private IVirtualSensorCallback mVirtualSensorCallback;
+
+        private static class VirtualSensorCallbackDelegate extends IVirtualSensorCallback.Stub {
+            @NonNull
+            private final Executor mExecutor;
+            @NonNull
+            private final VirtualSensorCallback mCallback;
+
+            VirtualSensorCallbackDelegate(@NonNull @CallbackExecutor Executor executor,
+                    @NonNull VirtualSensorCallback callback) {
+                mCallback = callback;
+                mExecutor = executor;
+            }
+
+            @Override
+            public void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled,
+                    int samplingPeriodMicros, int batchReportLatencyMicros) {
+                final Duration samplingPeriod =
+                        Duration.ofNanos(MICROSECONDS.toNanos(samplingPeriodMicros));
+                final Duration batchReportingLatency =
+                        Duration.ofNanos(MICROSECONDS.toNanos(batchReportLatencyMicros));
+                mExecutor.execute(() -> mCallback.onConfigurationChanged(
+                        sensor, enabled, samplingPeriod, batchReportingLatency));
+            }
+        }
+
         /**
          * Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
          * is required if this is set to {@link #LOCK_STATE_ALWAYS_UNLOCKED}.
@@ -731,6 +783,24 @@
         }
 
         /**
+         * Sets the callback to get notified about changes in the sensor listeners.
+         *
+         * @param executor The executor where the callback is executed on.
+         * @param callback The callback to get notified when the state of the sensor
+         * listeners has changed, see {@link VirtualSensorCallback}
+         */
+        @SuppressLint("MissingGetterMatchingBuilder")
+        @NonNull
+        public Builder setVirtualSensorCallback(
+                @NonNull @CallbackExecutor Executor executor,
+                @NonNull VirtualSensorCallback callback) {
+            mVirtualSensorCallback = new VirtualSensorCallbackDelegate(
+                    Objects.requireNonNull(executor),
+                    Objects.requireNonNull(callback));
+            return this;
+        }
+
+        /**
          * Sets the policy to indicate how activities are handled in recents.
          *
          * @param defaultRecentsPolicy A policy specifying how to handle activities in recents.
@@ -798,12 +868,17 @@
          */
         @NonNull
         public VirtualDeviceParams build() {
-            if (!mVirtualSensorConfigs.isEmpty()
-                    && (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT)
-                            != DEVICE_POLICY_CUSTOM)) {
-                throw new IllegalArgumentException(
-                        "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating "
-                                + "virtual sensors.");
+            if (!mVirtualSensorConfigs.isEmpty()) {
+                if (mDevicePolicies.get(POLICY_TYPE_SENSORS, DEVICE_POLICY_DEFAULT)
+                        != DEVICE_POLICY_CUSTOM) {
+                    throw new IllegalArgumentException(
+                            "DEVICE_POLICY_CUSTOM for POLICY_TYPE_SENSORS is required for creating "
+                                    + "virtual sensors.");
+                }
+                if (mVirtualSensorCallback == null) {
+                    throw new IllegalArgumentException(
+                            "VirtualSensorCallback is required for creating virtual sensors.");
+                }
             }
 
             if ((mAudioPlaybackSessionId != AUDIO_SESSION_ID_GENERATE
@@ -837,6 +912,7 @@
                     mName,
                     mDevicePolicies,
                     mVirtualSensorConfigs,
+                    mVirtualSensorCallback,
                     mDefaultRecentsPolicy,
                     mAudioPlaybackSessionId,
                     mAudioRecordingSessionId);
diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorStateChangeCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
similarity index 66%
rename from core/java/android/companion/virtual/sensor/IVirtualSensorStateChangeCallback.aidl
rename to core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
index b99cc7e..7da9c32 100644
--- a/core/java/android/companion/virtual/sensor/IVirtualSensorStateChangeCallback.aidl
+++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
@@ -16,20 +16,24 @@
 
 package android.companion.virtual.sensor;
 
+import android.companion.virtual.sensor.VirtualSensor;
+
 /**
- * Interface for notification of listener registration changes for a virtual sensor.
+ * Interface for notifying the sensor owner about whether and how sensor events should be injected.
  *
  * @hide
  */
-oneway interface IVirtualSensorStateChangeCallback {
+oneway interface IVirtualSensorCallback {
 
     /**
-     * Called when the registered listeners to a virtual sensor have changed.
+     * Called when the requested sensor event injection parameters have changed.
      *
+     * @param sensor The sensor whose requested injection parameters have changed.
      * @param enabled Whether the sensor is enabled.
      * @param samplingPeriodMicros The requested sensor's sampling period in microseconds.
      * @param batchReportingLatencyMicros The requested maximum time interval in microseconds
      * between the delivery of two batches of sensor events.
      */
-    void onStateChanged(boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros);
+    void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros,
+            int batchReportLatencyMicros);
 }
diff --git a/core/java/android/nfc/BeamShareData.aidl b/core/java/android/companion/virtual/sensor/VirtualSensor.aidl
similarity index 81%
copy from core/java/android/nfc/BeamShareData.aidl
copy to core/java/android/companion/virtual/sensor/VirtualSensor.aidl
index a47e240..ccb597a 100644
--- a/core/java/android/nfc/BeamShareData.aidl
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.nfc;
+package android.companion.virtual.sensor;
 
-parcelable BeamShareData;
+parcelable VirtualSensor;
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 58a5387..bda44d4 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -22,10 +22,10 @@
 import android.companion.virtual.IVirtualDevice;
 import android.hardware.Sensor;
 import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 
-import java.time.Duration;
-
 /**
  * Representation of a sensor on a remote device, capable of sending events, such as an
  * accelerometer or a gyroscope.
@@ -35,24 +35,8 @@
  * @hide
  */
 @SystemApi
-public class VirtualSensor {
-
-    /**
-     * Interface for notification of listener registration changes for a virtual sensor.
-     */
-    public interface SensorStateChangeCallback {
-        /**
-         * Called when the registered listeners to a virtual sensor have changed.
-         *
-         * @param enabled Whether the sensor is enabled.
-         * @param samplingPeriod The requested sampling period of the sensor.
-         * @param batchReportLatency The requested maximum time interval between the delivery of two
-         * batches of sensor events.
-         */
-        void onStateChanged(boolean enabled, @NonNull Duration samplingPeriod,
-                @NonNull Duration batchReportLatency);
-    }
-
+public final class VirtualSensor implements Parcelable {
+    private final int mHandle;
     private final int mType;
     private final String mName;
     private final IVirtualDevice mVirtualDevice;
@@ -61,13 +45,32 @@
     /**
      * @hide
      */
-    public VirtualSensor(int type, String name, IVirtualDevice virtualDevice, IBinder token) {
+    public VirtualSensor(int handle, int type, String name, IVirtualDevice virtualDevice,
+            IBinder token) {
+        mHandle = handle;
         mType = type;
         mName = name;
         mVirtualDevice = virtualDevice;
         mToken = token;
     }
 
+    private VirtualSensor(Parcel parcel) {
+        mHandle = parcel.readInt();
+        mType = parcel.readInt();
+        mName = parcel.readString8();
+        mVirtualDevice = IVirtualDevice.Stub.asInterface(parcel.readStrongBinder());
+        mToken = parcel.readStrongBinder();
+    }
+
+    /**
+     * Returns the unique handle of the sensor.
+     *
+     * @hide
+     */
+    public int getHandle() {
+        return mHandle;
+    }
+
     /**
      * Returns the type of the sensor.
      *
@@ -87,6 +90,32 @@
     }
 
     /**
+     * Returns the identifier of the
+     * {@link android.companion.virtual.VirtualDeviceManager.VirtualDevice} this sensor belongs to.
+     */
+    public int getDeviceId() {
+        try {
+            return mVirtualDevice.getDeviceId();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeInt(mHandle);
+        parcel.writeInt(mType);
+        parcel.writeString8(mName);
+        parcel.writeStrongBinder(mVirtualDevice.asBinder());
+        parcel.writeStrongBinder(mToken);
+    }
+
+    /**
      * Send a sensor event to the system.
      */
     @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -97,4 +126,16 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    @NonNull
+    public static final Parcelable.Creator<VirtualSensor> CREATOR =
+            new Parcelable.Creator<VirtualSensor>() {
+                public VirtualSensor createFromParcel(Parcel in) {
+                    return new VirtualSensor(in);
+                }
+
+                public VirtualSensor[] newArray(int size) {
+                    return new VirtualSensor[size];
+                }
+            };
 }
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
new file mode 100644
index 0000000..e097189
--- /dev/null
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2023 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.companion.virtual.sensor;
+
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.time.Duration;
+
+/**
+ * Interface for notifying the sensor owner about whether and how sensor events should be injected.
+ *
+ * <p>This callback can be used for controlling the sensor event injection - e.g. if the sensor is
+ * not enabled, then no events should be injected. Similarly, the rate and delay of the injected
+ * events that the registered listeners expect are specified here.
+ *
+ * <p>The callback is tied to the VirtualDevice's lifetime as the virtual sensors are created when
+ * the device is created and destroyed when the device is destroyed.
+ *
+ * @hide
+ */
+@SystemApi
+public interface VirtualSensorCallback {
+    /**
+     * Called when the requested sensor event injection parameters have changed.
+     *
+     * <p>This is effectively called when the registered listeners to a virtual sensor have changed.
+     *
+     * @param sensor The sensor whose requested injection parameters have changed.
+     * @param enabled Whether the sensor is enabled. True if any listeners are currently registered,
+     * and false otherwise.
+     * @param samplingPeriod The requested sampling period of the sensor.
+     * @param batchReportLatency The requested maximum time interval between the delivery of two
+     * batches of sensor events.
+     */
+    void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled,
+            @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency);
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index eb2f9dd..6d45365 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -16,20 +16,15 @@
 
 package android.companion.virtual.sensor;
 
-import static java.util.concurrent.TimeUnit.MICROSECONDS;
 
-import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.hardware.Sensor;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.time.Duration;
 import java.util.Objects;
-import java.util.concurrent.Executor;
 
 /**
  * Configuration for creation of a virtual sensor.
@@ -44,23 +39,17 @@
     private final String mName;
     @Nullable
     private final String mVendor;
-    @Nullable
-    private final IVirtualSensorStateChangeCallback mStateChangeCallback;
 
-    private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor,
-            @Nullable IVirtualSensorStateChangeCallback stateChangeCallback) {
+    private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor) {
         mType = type;
         mName = name;
         mVendor = vendor;
-        mStateChangeCallback = stateChangeCallback;
     }
 
     private VirtualSensorConfig(@NonNull Parcel parcel) {
         mType = parcel.readInt();
         mName = parcel.readString8();
         mVendor = parcel.readString8();
-        mStateChangeCallback =
-                IVirtualSensorStateChangeCallback.Stub.asInterface(parcel.readStrongBinder());
     }
 
     @Override
@@ -73,8 +62,6 @@
         parcel.writeInt(mType);
         parcel.writeString8(mName);
         parcel.writeString8(mVendor);
-        parcel.writeStrongBinder(
-                mStateChangeCallback != null ? mStateChangeCallback.asBinder() : null);
     }
 
     /**
@@ -105,15 +92,6 @@
     }
 
     /**
-     * Returns the callback to get notified about changes in the sensor listeners.
-     * @hide
-     */
-    @Nullable
-    public IVirtualSensorStateChangeCallback getStateChangeCallback() {
-        return mStateChangeCallback;
-    }
-
-    /**
      * Builder for {@link VirtualSensorConfig}.
      */
     public static final class Builder {
@@ -123,32 +101,6 @@
         private final String mName;
         @Nullable
         private String mVendor;
-        @Nullable
-        private IVirtualSensorStateChangeCallback mStateChangeCallback;
-
-        private static class SensorStateChangeCallbackDelegate
-                extends IVirtualSensorStateChangeCallback.Stub {
-            @NonNull
-            private final Executor mExecutor;
-            @NonNull
-            private final VirtualSensor.SensorStateChangeCallback mCallback;
-
-            SensorStateChangeCallbackDelegate(@NonNull @CallbackExecutor Executor executor,
-                    @NonNull VirtualSensor.SensorStateChangeCallback callback) {
-                mCallback = callback;
-                mExecutor = executor;
-            }
-            @Override
-            public void onStateChanged(boolean enabled, int samplingPeriodMicros,
-                    int batchReportLatencyMicros) {
-                final Duration samplingPeriod =
-                        Duration.ofNanos(MICROSECONDS.toNanos(samplingPeriodMicros));
-                final Duration batchReportingLatency =
-                        Duration.ofNanos(MICROSECONDS.toNanos(batchReportLatencyMicros));
-                mExecutor.execute(() -> mCallback.onStateChanged(
-                        enabled, samplingPeriod, batchReportingLatency));
-            }
-        }
 
         /**
          * Creates a new builder.
@@ -167,7 +119,7 @@
          */
         @NonNull
         public VirtualSensorConfig build() {
-            return new VirtualSensorConfig(mType, mName, mVendor, mStateChangeCallback);
+            return new VirtualSensorConfig(mType, mName, mVendor);
         }
 
         /**
@@ -178,24 +130,6 @@
             mVendor = vendor;
             return this;
         }
-
-        /**
-         * Sets the callback to get notified about changes in the sensor listeners.
-         *
-         * @param executor The executor where the callback is executed on.
-         * @param callback The callback to get notified when the state of the sensor
-         * listeners has changed, see {@link VirtualSensor.SensorStateChangeCallback}
-         */
-        @SuppressLint("MissingGetterMatchingBuilder")
-        @NonNull
-        public VirtualSensorConfig.Builder setStateChangeCallback(
-                @NonNull @CallbackExecutor Executor executor,
-                @NonNull VirtualSensor.SensorStateChangeCallback callback) {
-            mStateChangeCallback = new SensorStateChangeCallbackDelegate(
-                    Objects.requireNonNull(executor),
-                    Objects.requireNonNull(callback));
-            return this;
-        }
     }
 
     @NonNull
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dfaa065..baf10ed 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -575,8 +575,6 @@
     boolean performDexOptSecondary(String packageName,
             String targetCompilerFilter, boolean force);
 
-    void forceDexOpt(String packageName);
-
     int getMoveStatus(int moveId);
 
     void registerMoveCallback(in IPackageMoveObserver callback);
diff --git a/core/java/android/content/pm/ServiceInfo.java b/core/java/android/content/pm/ServiceInfo.java
index 49d21da..90ed4ce 100644
--- a/core/java/android/content/pm/ServiceInfo.java
+++ b/core/java/android/content/pm/ServiceInfo.java
@@ -246,8 +246,17 @@
 
     /**
      * Constant corresponding to {@code mediaProjection} in
-     * the {@link android.R.attr#foregroundServiceType} attribute.
-     * Managing a media projection session, e.g for screen recording or taking screenshots.
+     * the {@link android.R.attr#foregroundServiceType foregroundServiceType} attribute.
+     *
+     * <p>
+     * To capture through {@link android.media.projection.MediaProjection}, an app must start a
+     * foreground service with the type corresponding to this constant. This type should only be
+     * used for {@link android.media.projection.MediaProjection}. Capturing screen contents via
+     * {@link android.media.projection.MediaProjection#createVirtualDisplay(String, int, int, int,
+     * int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback,
+     * android.os.Handler) createVirtualDisplay} conveniently allows recording, presenting screen
+     * contents into a meeting, taking screenshots, or several other scenarios.
+     * </p>
      *
      * <p>Starting foreground service with this type from apps targeting API level
      * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and later, will require permission
diff --git a/core/java/android/hardware/usb/DisplayPortAltModeInfo.java b/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
index 7c62373..9da2f4c 100644
--- a/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
+++ b/core/java/android/hardware/usb/DisplayPortAltModeInfo.java
@@ -79,7 +79,7 @@
      * as one of its capabilities, however may not yet have entered DisplayPort Alt Mode or has been
      * configured for data transmission.
      */
-    public static final int DISPLAYPORT_ALT_MODE_STATUS_CAPABLE = 2;
+    public static final int DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED = 2;
 
     /**
      * Port Partners:
@@ -113,7 +113,7 @@
     @IntDef(prefix = { "DISPLAYPORT_ALT_MODE_STATUS_" }, value = {
             DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN,
             DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE,
-            DISPLAYPORT_ALT_MODE_STATUS_CAPABLE,
+            DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED,
             DISPLAYPORT_ALT_MODE_STATUS_ENABLED,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -150,10 +150,6 @@
     /**
      * Returns the DisplayPort Alt Mode Status for a port partner acting as a sink.
      *
-     * @return {@link #DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN}
-     *        or {@link #DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE}
-     *        or {@link #DISPLAYPORT_ALT_MODE_STATUS_CAPABLE}
-     *        or {@link #DISPLAYPORT_ALT_MODE_STATUS_ENABLED}
      */
     public @DisplayPortAltModeStatus int getPartnerSinkStatus() {
         return mPartnerSinkStatus;
@@ -162,10 +158,6 @@
     /**
      * Returns the DisplayPort Alt Mode Status for the attached cable
      *
-     * @return {@link #DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN}
-     *        or {@link #DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE}
-     *        or {@link #DISPLAYPORT_ALT_MODE_STATUS_CAPABLE}
-     *        or {@link #DISPLAYPORT_ALT_MODE_STATUS_ENABLED}
      */
     public @DisplayPortAltModeStatus int getCableStatus() {
         return mCableStatus;
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 71ec1c6..889d3df 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -1647,7 +1647,7 @@
     /**
      * Registers the given listener to listen for DisplayPort Alt Mode changes.
      * <p>
-     * If this method returns true, the caller should ensure to call
+     * If this method returns without Exceptions, the caller should ensure to call
      * {@link #unregisterDisplayPortAltModeListener} when it no longer requires updates.
      *
      * @param executor          Executor on which to run the listener.
@@ -1655,14 +1655,14 @@
      *                          changes. See {@link #DisplayPortAltModeInfoListener} for listener
      *                          details.
      *
-     * @return true on successful register, false on failed register due to listener already being
-     *         registered or an internal error.
+     * @throws IllegalStateException if listener has already been registered previously but not
+     * unregistered or an unexpected system failure occurs.
      *
      * @hide
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.MANAGE_USB)
-    public boolean registerDisplayPortAltModeInfoListener(
+    public void registerDisplayPortAltModeInfoListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull DisplayPortAltModeInfoListener listener) {
         Objects.requireNonNull(executor, "registerDisplayPortAltModeInfoListener: "
@@ -1678,15 +1678,15 @@
 
             if (mDisplayPortServiceListener == null) {
                 if (!registerDisplayPortAltModeEventsIfNeededLocked()) {
-                    return false;
+                    throw new IllegalStateException("Unexpected failure registering service "
+                            + "listener");
                 }
             }
             if (mDisplayPortListeners.containsKey(listener)) {
-                return false;
+                throw new IllegalStateException("Listener has already been registered.");
             }
 
             mDisplayPortListeners.put(listener, executor);
-            return true;
         }
     }
 
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 73dcb36..490b128 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -54,7 +54,7 @@
 import static android.hardware.usb.UsbPortStatus.COMPLIANCE_WARNING_OTHER;
 import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_UNKNOWN;
 import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE;
-import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_CAPABLE;
+import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED;
 import static android.hardware.usb.DisplayPortAltModeInfo.DISPLAYPORT_ALT_MODE_STATUS_ENABLED;
 
 import android.Manifest;
@@ -807,8 +807,8 @@
                 return "Unknown";
             case DISPLAYPORT_ALT_MODE_STATUS_NOT_CAPABLE:
                 return "Not Capable";
-            case DISPLAYPORT_ALT_MODE_STATUS_CAPABLE:
-                return "Capable";
+            case DISPLAYPORT_ALT_MODE_STATUS_CAPABLE_DISABLED:
+                return "Capable-Disabled";
             case DISPLAYPORT_ALT_MODE_STATUS_ENABLED:
                 return "Enabled";
             default:
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index 8c13307..e1662b8 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -311,7 +311,7 @@
 
     /**
      * Indicates that the Type-C plug orientation cannot be
-     * determined.
+     * determined because the connected state of the device is unknown.
      */
     public static final int PLUG_STATE_UNKNOWN = 0;
 
@@ -600,13 +600,8 @@
     }
 
     /**
-     * Returns the orientation state of the attached cable/adapter.
+     * Returns the plug state of the attached cable/adapter.
      *
-     * @return one of {@link #PLUG_STATE_UNKNOWN},
-     *                {@link #PLUG_STATE_UNPLUGGED},
-     *                {@link #PLUG_STATE_PLUGGED_ORIENTATION_UNKNOWN},
-     *                {@link #PLUG_STATE_PLUGGED_ORIENTATION_NORMAL},
-     *                {@link #PLUG_STATE_PLUGGED_ORIENTATION_FLIPPED},
      */
     public @PlugState int getPlugState() {
         return mPlugState;
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
deleted file mode 100644
index 6a40f98..0000000
--- a/core/java/android/nfc/BeamShareData.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package android.nfc;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.UserHandle;
-
-/**
- * Class to IPC data to be shared over Android Beam.
- * Allows bundling NdefMessage, Uris and flags in a single
- * IPC call. This is important as we want to reduce the
- * amount of IPC calls at "touch time".
- * @hide
- */
-public final class BeamShareData implements Parcelable {
-    public final NdefMessage ndefMessage;
-    public final Uri[] uris;
-    public final UserHandle userHandle;
-    public final int flags;
-
-    public BeamShareData(NdefMessage msg, Uri[] uris, UserHandle userHandle, int flags) {
-        this.ndefMessage = msg;
-        this.uris = uris;
-        this.userHandle = userHandle;
-        this.flags = flags;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        int urisLength = (uris != null) ? uris.length : 0;
-        dest.writeParcelable(ndefMessage, 0);
-        dest.writeInt(urisLength);
-        if (urisLength > 0) {
-            dest.writeTypedArray(uris, 0);
-        }
-        dest.writeParcelable(userHandle, 0);
-        dest.writeInt(this.flags);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<BeamShareData> CREATOR =
-            new Parcelable.Creator<BeamShareData>() {
-        @Override
-        public BeamShareData createFromParcel(Parcel source) {
-            Uri[] uris = null;
-            NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader(), android.nfc.NdefMessage.class);
-            int numUris = source.readInt();
-            if (numUris > 0) {
-                uris = new Uri[numUris];
-                source.readTypedArray(uris, Uri.CREATOR);
-            }
-            UserHandle userHandle = source.readParcelable(UserHandle.class.getClassLoader(), android.os.UserHandle.class);
-            int flags = source.readInt();
-
-            return new BeamShareData(msg, uris, userHandle, flags);
-        }
-
-        @Override
-        public BeamShareData[] newArray(int size) {
-            return new BeamShareData[size];
-        }
-    };
-}
diff --git a/core/java/android/nfc/IAppCallback.aidl b/core/java/android/nfc/IAppCallback.aidl
index 133146d..b06bf06 100644
--- a/core/java/android/nfc/IAppCallback.aidl
+++ b/core/java/android/nfc/IAppCallback.aidl
@@ -16,7 +16,6 @@
 
 package android.nfc;
 
-import android.nfc.BeamShareData;
 import android.nfc.Tag;
 
 /**
@@ -24,7 +23,5 @@
  */
 interface IAppCallback
 {
-    BeamShareData createBeamShareData(byte peerLlcpVersion);
-    oneway void onNdefPushComplete(byte peerLlcpVersion);
     oneway void onTagDiscovered(in Tag tag);
 }
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 8a30ef4..a6d8caf 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -18,7 +18,6 @@
 
 import android.app.PendingIntent;
 import android.content.IntentFilter;
-import android.nfc.BeamShareData;
 import android.nfc.NdefMessage;
 import android.nfc.Tag;
 import android.nfc.TechListParcel;
@@ -47,24 +46,18 @@
     int getState();
     boolean disable(boolean saveState);
     boolean enable();
-    boolean enableNdefPush();
-    boolean disableNdefPush();
-    boolean isNdefPushEnabled();
     void pausePolling(int timeoutInMs);
     void resumePolling();
 
     void setForegroundDispatch(in PendingIntent intent,
             in IntentFilter[] filters, in TechListParcel techLists);
     void setAppCallback(in IAppCallback callback);
-    oneway void invokeBeam();
-    oneway void invokeBeamInternal(in BeamShareData shareData);
 
     boolean ignore(int nativeHandle, int debounceMs, ITagRemovedCallback callback);
 
     void dispatch(in Tag tag);
 
     void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
-    void setP2pModes(int initatorModes, int targetModes);
 
     void addNfcUnlockHandler(INfcUnlockHandler unlockHandler, in int[] techList);
     void removeNfcUnlockHandler(INfcUnlockHandler unlockHandler);
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 911aaf3..8d75cac 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -19,9 +19,6 @@
 import android.app.Activity;
 import android.app.Application;
 import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ContentProvider;
-import android.content.Intent;
-import android.net.Uri;
 import android.nfc.NfcAdapter.ReaderCallback;
 import android.os.Binder;
 import android.os.Bundle;
@@ -110,14 +107,8 @@
     class NfcActivityState {
         boolean resumed = false;
         Activity activity;
-        NdefMessage ndefMessage = null;  // static NDEF message
-        NfcAdapter.CreateNdefMessageCallback ndefMessageCallback = null;
-        NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
-        NfcAdapter.CreateBeamUrisCallback uriCallback = null;
-        Uri[] uris = null;
-        int flags = 0;
-        int readerModeFlags = 0;
         NfcAdapter.ReaderCallback readerCallback = null;
+        int readerModeFlags = 0;
         Bundle readerModeExtras = null;
         Binder token;
 
@@ -137,24 +128,16 @@
             unregisterApplication(activity.getApplication());
             resumed = false;
             activity = null;
-            ndefMessage = null;
-            ndefMessageCallback = null;
-            onNdefPushCompleteCallback = null;
-            uriCallback = null;
-            uris = null;
+            readerCallback = null;
             readerModeFlags = 0;
+            readerModeExtras = null;
             token = null;
         }
         @Override
         public String toString() {
-            StringBuilder s = new StringBuilder("[").append(" ");
-            s.append(ndefMessage).append(" ").append(ndefMessageCallback).append(" ");
-            s.append(uriCallback).append(" ");
-            if (uris != null) {
-                for (Uri uri : uris) {
-                    s.append(onNdefPushCompleteCallback).append(" ").append(uri).append("]");
-                }
-            }
+            StringBuilder s = new StringBuilder("[");
+            s.append(readerCallback);
+            s.append("]");
             return s.toString();
         }
     }
@@ -245,92 +228,6 @@
         }
     }
 
-    public void setNdefPushContentUri(Activity activity, Uri[] uris) {
-        boolean isResumed;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = getActivityState(activity);
-            state.uris = uris;
-            isResumed = state.resumed;
-        }
-        if (isResumed) {
-            // requestNfcServiceCallback() verifies permission also
-            requestNfcServiceCallback();
-        } else {
-            // Crash API calls early in case NFC permission is missing
-            verifyNfcPermission();
-        }
-    }
-
-
-    public void setNdefPushContentUriCallback(Activity activity,
-            NfcAdapter.CreateBeamUrisCallback callback) {
-        boolean isResumed;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = getActivityState(activity);
-            state.uriCallback = callback;
-            isResumed = state.resumed;
-        }
-        if (isResumed) {
-            // requestNfcServiceCallback() verifies permission also
-            requestNfcServiceCallback();
-        } else {
-            // Crash API calls early in case NFC permission is missing
-            verifyNfcPermission();
-        }
-    }
-
-    public void setNdefPushMessage(Activity activity, NdefMessage message, int flags) {
-        boolean isResumed;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = getActivityState(activity);
-            state.ndefMessage = message;
-            state.flags = flags;
-            isResumed = state.resumed;
-        }
-        if (isResumed) {
-            // requestNfcServiceCallback() verifies permission also
-            requestNfcServiceCallback();
-        } else {
-            // Crash API calls early in case NFC permission is missing
-            verifyNfcPermission();
-        }
-    }
-
-    public void setNdefPushMessageCallback(Activity activity,
-            NfcAdapter.CreateNdefMessageCallback callback, int flags) {
-        boolean isResumed;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = getActivityState(activity);
-            state.ndefMessageCallback = callback;
-            state.flags = flags;
-            isResumed = state.resumed;
-        }
-        if (isResumed) {
-            // requestNfcServiceCallback() verifies permission also
-            requestNfcServiceCallback();
-        } else {
-            // Crash API calls early in case NFC permission is missing
-            verifyNfcPermission();
-        }
-    }
-
-    public void setOnNdefPushCompleteCallback(Activity activity,
-            NfcAdapter.OnNdefPushCompleteCallback callback) {
-        boolean isResumed;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = getActivityState(activity);
-            state.onNdefPushCompleteCallback = callback;
-            isResumed = state.resumed;
-        }
-        if (isResumed) {
-            // requestNfcServiceCallback() verifies permission also
-            requestNfcServiceCallback();
-        } else {
-            // Crash API calls early in case NFC permission is missing
-            verifyNfcPermission();
-        }
-    }
-
     /**
      * Request or unrequest NFC service callbacks.
      * Makes IPC call - do not hold lock.
@@ -351,86 +248,6 @@
         }
     }
 
-    /** Callback from NFC service, usually on binder thread */
-    @Override
-    public BeamShareData createBeamShareData(byte peerLlcpVersion) {
-        NfcAdapter.CreateNdefMessageCallback ndefCallback;
-        NfcAdapter.CreateBeamUrisCallback urisCallback;
-        NdefMessage message;
-        Activity activity;
-        Uri[] uris;
-        int flags;
-        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = findResumedActivityState();
-            if (state == null) return null;
-
-            ndefCallback = state.ndefMessageCallback;
-            urisCallback = state.uriCallback;
-            message = state.ndefMessage;
-            uris = state.uris;
-            flags = state.flags;
-            activity = state.activity;
-        }
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            // Make callbacks without lock
-            if (ndefCallback != null) {
-                message = ndefCallback.createNdefMessage(event);
-            }
-            if (urisCallback != null) {
-                uris = urisCallback.createBeamUris(event);
-                if (uris != null) {
-                    ArrayList<Uri> validUris = new ArrayList<Uri>();
-                    for (Uri uri : uris) {
-                        if (uri == null) {
-                            Log.e(TAG, "Uri not allowed to be null.");
-                            continue;
-                        }
-                        String scheme = uri.getScheme();
-                        if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
-                                !scheme.equalsIgnoreCase("content"))) {
-                            Log.e(TAG, "Uri needs to have " +
-                                    "either scheme file or scheme content");
-                            continue;
-                        }
-                        uri = ContentProvider.maybeAddUserId(uri, activity.getUserId());
-                        validUris.add(uri);
-                    }
-
-                    uris = validUris.toArray(new Uri[validUris.size()]);
-                }
-            }
-            if (uris != null && uris.length > 0) {
-                for (Uri uri : uris) {
-                    // Grant the NFC process permission to read these URIs
-                    activity.grantUriPermission("com.android.nfc", uri,
-                            Intent.FLAG_GRANT_READ_URI_PERMISSION);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-        return new BeamShareData(message, uris, activity.getUser(), flags);
-    }
-
-    /** Callback from NFC service, usually on binder thread */
-    @Override
-    public void onNdefPushComplete(byte peerLlcpVersion) {
-        NfcAdapter.OnNdefPushCompleteCallback callback;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = findResumedActivityState();
-            if (state == null) return;
-
-            callback = state.onNdefPushCompleteCallback;
-        }
-        NfcEvent event = new NfcEvent(mAdapter, peerLlcpVersion);
-        // Make callback without lock
-        if (callback != null) {
-            callback.onNdefPushComplete(event);
-        }
-    }
-
     @Override
     public void onTagDiscovered(Tag tag) throws RemoteException {
         NfcAdapter.ReaderCallback callback;
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 1bb44af..4a244c0 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -343,8 +343,12 @@
      */
     public static final String EXTRA_READER_PRESENCE_CHECK_DELAY = "presence";
 
-    /** @hide */
+    /**
+     * @hide
+     * @removed
+     */
     @SystemApi
+    @UnsupportedAppUsage
     public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
 
     /** @hide */
@@ -418,7 +422,6 @@
     // Guarded by NfcAdapter.class
     static boolean sIsInitialized = false;
     static boolean sHasNfcFeature;
-    static boolean sHasBeamFeature;
 
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
@@ -483,7 +486,7 @@
      * A callback to be invoked when the system successfully delivers your {@link NdefMessage}
      * to another device.
      * @see #setOnNdefPushCompleteCallback
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @deprecated this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -509,7 +512,7 @@
      * content currently visible to the user. Alternatively, you can call {@link
      * #setNdefPushMessage setNdefPushMessage()} if the {@link NdefMessage} always contains the
      * same data.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @deprecated this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -539,7 +542,7 @@
 
 
      /**
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @deprecated this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
@@ -615,7 +618,6 @@
             PackageManager pm;
             pm = context.getPackageManager();
             sHasNfcFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC);
-            sHasBeamFeature = pm.hasSystemFeature(PackageManager.FEATURE_NFC_BEAM);
             boolean hasHceFeature =
                     pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION)
                     || pm.hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF);
@@ -1111,35 +1113,17 @@
      * @param uris an array of Uri(s) to push over Android Beam
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+    @UnsupportedAppUsage
     public void setBeamPushUris(Uri[] uris, Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
         }
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        if (uris != null) {
-            for (Uri uri : uris) {
-                if (uri == null) throw new NullPointerException("Uri not " +
-                        "allowed to be null");
-                String scheme = uri.getScheme();
-                if (scheme == null || (!scheme.equalsIgnoreCase("file") &&
-                        !scheme.equalsIgnoreCase("content"))) {
-                    throw new IllegalArgumentException("URI needs to have " +
-                            "either scheme file or scheme content");
-                }
-            }
-        }
-        mNfcActivityManager.setNdefPushContentUri(activity, uris);
     }
 
     /**
@@ -1199,23 +1183,17 @@
      * @param callback callback, or null to disable
      * @param activity activity for which the Uri(s) will be pushed
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+    @UnsupportedAppUsage
     public void setBeamPushUrisCallback(CreateBeamUrisCallback callback, Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
         }
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        mNfcActivityManager.setNdefPushContentUriCallback(activity, callback);
     }
 
     /**
@@ -1289,58 +1267,32 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+    @UnsupportedAppUsage
     public void setNdefPushMessage(NdefMessage message, Activity activity,
             Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
-        }
-        int targetSdkVersion = getSdkVersion();
-        try {
-            if (activity == null) {
-                throw new NullPointerException("activity cannot be null");
-            }
-            mNfcActivityManager.setNdefPushMessage(activity, message, 0);
-            for (Activity a : activities) {
-                if (a == null) {
-                    throw new NullPointerException("activities cannot contain null");
-                }
-                mNfcActivityManager.setNdefPushMessage(a, message, 0);
-            }
-        } catch (IllegalStateException e) {
-            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
-                // Less strict on old applications - just log the error
-                Log.e(TAG, "Cannot call API with Activity that has already " +
-                        "been destroyed", e);
-            } else {
-                // Prevent new applications from making this mistake, re-throw
-                throw(e);
-            }
         }
     }
 
     /**
      * @hide
+     * @removed
      */
     @SystemApi
+    @UnsupportedAppUsage
     public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
         }
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        mNfcActivityManager.setNdefPushMessage(activity, message, flags);
     }
 
     /**
@@ -1408,54 +1360,18 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+    @UnsupportedAppUsage
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
         }
-        int targetSdkVersion = getSdkVersion();
-        try {
-            if (activity == null) {
-                throw new NullPointerException("activity cannot be null");
-            }
-            mNfcActivityManager.setNdefPushMessageCallback(activity, callback, 0);
-            for (Activity a : activities) {
-                if (a == null) {
-                    throw new NullPointerException("activities cannot contain null");
-                }
-                mNfcActivityManager.setNdefPushMessageCallback(a, callback, 0);
-            }
-        } catch (IllegalStateException e) {
-            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
-                // Less strict on old applications - just log the error
-                Log.e(TAG, "Cannot call API with Activity that has already " +
-                        "been destroyed", e);
-            } else {
-                // Prevent new applications from making this mistake, re-throw
-                throw(e);
-            }
-        }
-    }
-
-    /**
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
-            int flags) {
-        if (activity == null) {
-            throw new NullPointerException("activity cannot be null");
-        }
-        mNfcActivityManager.setNdefPushMessageCallback(activity, callback, flags);
     }
 
     /**
@@ -1495,41 +1411,17 @@
      *        to only register one at a time, and to do so in that activity's
      *        {@link Activity#onCreate}
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+    @UnsupportedAppUsage
     public void setOnNdefPushCompleteCallback(OnNdefPushCompleteCallback callback,
             Activity activity, Activity ... activities) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
-        }
-        int targetSdkVersion = getSdkVersion();
-        try {
-            if (activity == null) {
-                throw new NullPointerException("activity cannot be null");
-            }
-            mNfcActivityManager.setOnNdefPushCompleteCallback(activity, callback);
-            for (Activity a : activities) {
-                if (a == null) {
-                    throw new NullPointerException("activities cannot contain null");
-                }
-                mNfcActivityManager.setOnNdefPushCompleteCallback(a, callback);
-            }
-        } catch (IllegalStateException e) {
-            if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
-                // Less strict on old applications - just log the error
-                Log.e(TAG, "Cannot call API with Activity that has already " +
-                        "been destroyed", e);
-            } else {
-                // Prevent new applications from making this mistake, re-throw
-                throw(e);
-            }
         }
     }
 
@@ -1712,46 +1604,18 @@
      * @param activity the current foreground Activity that has registered data to share
      * @return whether the Beam animation was successfully invoked
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
+    @UnsupportedAppUsage
     public boolean invokeBeam(Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return false;
-            }
         }
-        if (activity == null) {
-            throw new NullPointerException("activity may not be null.");
-        }
-        enforceResumed(activity);
-        try {
-            sService.invokeBeam();
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "invokeBeam: NFC process has died.");
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public boolean invokeBeam(BeamShareData shareData) {
-        try {
-            Log.e(TAG, "invokeBeamInternal()");
-            sService.invokeBeamInternal(shareData);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "invokeBeam: NFC process has died.");
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
+        return false;
     }
 
     /**
@@ -1777,25 +1641,18 @@
      *
      * @param activity foreground activity
      * @param message a NDEF Message to push over NFC
-     * @throws IllegalStateException if the activity is not currently in the foreground
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated use {@link #setNdefPushMessage} instead
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void enableForegroundNdefPush(Activity activity, NdefMessage message) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
         }
-        if (activity == null || message == null) {
-            throw new NullPointerException();
-        }
-        enforceResumed(activity);
-        mNfcActivityManager.setNdefPushMessage(activity, message, 0);
     }
 
     /**
@@ -1814,27 +1671,18 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      *
      * @param activity the Foreground activity
-     * @throws IllegalStateException if the Activity has already been paused
-     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated use {@link #setNdefPushMessage} instead
+     * @throws UnsupportedOperationException if FEATURE_NFC is unavailable
+     * @removed this feature is removed. File sharing can work using other technology like
+     * Bluetooth.
      */
     @Deprecated
+    @UnsupportedAppUsage
     public void disableForegroundNdefPush(Activity activity) {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return;
-            }
         }
-        if (activity == null) {
-            throw new NullPointerException();
-        }
-        enforceResumed(activity);
-        mNfcActivityManager.setNdefPushMessage(activity, null, 0);
-        mNfcActivityManager.setNdefPushMessageCallback(activity, null, 0);
-        mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
     }
 
     /**
@@ -1959,40 +1807,26 @@
      * Enable NDEF Push feature.
      * <p>This API is for the Settings application.
      * @hide
+     * @removed
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @UnsupportedAppUsage
     public boolean enableNdefPush() {
-        if (!sHasNfcFeature) {
-            throw new UnsupportedOperationException();
-        }
-        try {
-            return sService.enableNdefPush();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
+        return false;
     }
 
     /**
      * Disable NDEF Push feature.
      * <p>This API is for the Settings application.
      * @hide
+     * @removed
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @UnsupportedAppUsage
     public boolean disableNdefPush() {
-        synchronized (NfcAdapter.class) {
-            if (!sHasNfcFeature) {
-                throw new UnsupportedOperationException();
-            }
-        }
-        try {
-            return sService.disableNdefPush();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
+        return false;
     }
 
     /**
@@ -2018,26 +1852,18 @@
      * @see android.provider.Settings#ACTION_NFCSHARING_SETTINGS
      * @return true if NDEF Push feature is enabled
      * @throws UnsupportedOperationException if FEATURE_NFC is unavailable.
-     * @deprecated this feature is deprecated. File sharing can work using other technology like
+     * @removed this feature is removed. File sharing can work using other technology like
      * Bluetooth.
      */
     @java.lang.Deprecated
-
+    @UnsupportedAppUsage
     public boolean isNdefPushEnabled() {
         synchronized (NfcAdapter.class) {
             if (!sHasNfcFeature) {
                 throw new UnsupportedOperationException();
             }
-            if (!sHasBeamFeature) {
-                return false;
-            }
         }
-        try {
-            return sService.isNdefPushEnabled();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
+        return false;
     }
 
     /**
@@ -2128,17 +1954,6 @@
     }
 
     /**
-     * @hide
-     */
-    public void setP2pModes(int initiatorModes, int targetModes) {
-        try {
-            sService.setP2pModes(initiatorModes, targetModes);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
-    }
-
-    /**
      * Registers a new NFC unlock handler with the NFC service.
      *
      * <p />NFC unlock handlers are intended to unlock the keyguard in the presence of a trusted
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 832f23c..9689be2 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -787,12 +787,9 @@
          * PackageManager.setComponentEnabledSetting} will now throw an
          * IllegalArgumentException if the given component class name does not
          * exist in the application's manifest.
-         * <li> {@link android.nfc.NfcAdapter#setNdefPushMessage
-         * NfcAdapter.setNdefPushMessage},
-         * {@link android.nfc.NfcAdapter#setNdefPushMessageCallback
-         * NfcAdapter.setNdefPushMessageCallback} and
-         * {@link android.nfc.NfcAdapter#setOnNdefPushCompleteCallback
-         * NfcAdapter.setOnNdefPushCompleteCallback} will throw
+         * <li> {@code NfcAdapter.setNdefPushMessage},
+         * {@code NfcAdapter.setNdefPushMessageCallback} and
+         * {@code NfcAdapter.setOnNdefPushCompleteCallback} will throw
          * IllegalStateException if called after the Activity has been destroyed.
          * <li> Accessibility services must require the new
          * {@link android.Manifest.permission#BIND_ACCESSIBILITY_SERVICE} permission or
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2142c4c..13d54ef 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -2994,6 +2994,7 @@
      */
     @IntDef(prefix = { "LOW_POWER_STANDBY_ALLOWED_REASON_" }, flag = true, value = {
             LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION,
+            LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface LowPowerStandbyAllowedReason {
@@ -3006,6 +3007,13 @@
      */
     public static final int LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION = 1 << 0;
 
+    /**
+     * Exempts apps on the temporary powersave allowlist.
+     *
+     * @see #isAllowedInLowPowerStandby(int)
+     */
+    public static final int LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST = 1 << 1;
+
     /** @hide */
     public static String lowPowerStandbyAllowedReasonsToString(
             @LowPowerStandbyAllowedReason int allowedReasons) {
@@ -3014,6 +3022,10 @@
             allowedStrings.add("ALLOWED_REASON_VOICE_INTERACTION");
             allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
         }
+        if ((allowedReasons & LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST) != 0) {
+            allowedStrings.add("ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST");
+            allowedReasons &= ~LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
+        }
         if (allowedReasons != 0) {
             allowedStrings.add(String.valueOf(allowedReasons));
         }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1df45d1..c60f558 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3612,7 +3612,7 @@
      *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
      * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
-     * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION}.
+     * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION.
      *
      * @param name     the user's name
      * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
@@ -3686,7 +3686,7 @@
      *
      * <p>Requires {@link android.Manifest.permission#MANAGE_USERS}.
      * {@link android.Manifest.permission#CREATE_USERS} suffices if flags are in
-     * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION}.
+     * com.android.server.pm.UserManagerService#ALLOWED_FLAGS_FOR_CREATE_USERS_PERMISSION.
      *
      * @param userType the type of user, such as {@link UserManager#USER_TYPE_FULL_GUEST}.
      * @return the {@link UserInfo} object for the created user.
@@ -3718,25 +3718,23 @@
      */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createGuest(Context context) {
+    public @Nullable UserInfo createGuest(Context context) {
         try {
             final UserInfo guest = mService.createUserWithThrow(null, USER_TYPE_FULL_GUEST, 0);
-            if (guest != null) {
-                Settings.Secure.putStringForUser(context.getContentResolver(),
-                        Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
+            Settings.Secure.putStringForUser(context.getContentResolver(),
+                    Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id);
 
-                if (UserManager.isGuestUserAllowEphemeralStateChange()) {
-                    // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1
-                    // This is done so that a user via a UI controller can choose to
-                    // make a guest as ephemeral or not.
-                    // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state
-                    // should be, with default being ephemeral.
-                    boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(),
-                                                 Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1;
+            if (UserManager.isGuestUserAllowEphemeralStateChange()) {
+                // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1
+                // This is done so that a user via a UI controller can choose to
+                // make a guest as ephemeral or not.
+                // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state
+                // should be, with default being ephemeral.
+                boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(),
+                                             Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1;
 
-                    if (resetGuestOnExit && !guest.isEphemeral()) {
-                        setUserEphemeral(guest.id, true);
-                    }
+                if (resetGuestOnExit && !guest.isEphemeral()) {
+                    setUserEphemeral(guest.id, true);
                 }
             }
             return guest;
@@ -3855,7 +3853,7 @@
      */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createProfileForUser(String name, @NonNull String userType,
+    public @Nullable UserInfo createProfileForUser(String name, @NonNull String userType,
             @UserInfoFlag int flags, @UserIdInt int userId) {
         return createProfileForUser(name, userType, flags, userId, null);
     }
@@ -3900,7 +3898,7 @@
      */
     @RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
             Manifest.permission.CREATE_USERS})
-    public UserInfo createProfileForUserEvenWhenDisallowed(String name,
+    public @Nullable UserInfo createProfileForUserEvenWhenDisallowed(String name,
             @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int userId,
             String[] disallowedPackages) {
         try {
@@ -3931,11 +3929,9 @@
         try {
             final int parentUserId = mUserId;
             final UserInfo profile = mService.createRestrictedProfileWithThrow(name, parentUserId);
-            if (profile != null) {
-                final UserHandle parentUserHandle = UserHandle.of(parentUserId);
-                AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
-                        UserHandle.of(profile.id));
-            }
+            final UserHandle parentUserHandle = UserHandle.of(parentUserId);
+            AccountManager.get(mContext).addSharedAccountsFromParentUser(parentUserHandle,
+                    UserHandle.of(profile.id));
             return profile;
         } catch (ServiceSpecificException e) {
             return null;
diff --git a/core/java/android/provider/DeviceConfigInitializer.java b/core/java/android/provider/DeviceConfigInitializer.java
new file mode 100644
index 0000000..d60449f
--- /dev/null
+++ b/core/java/android/provider/DeviceConfigInitializer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+
+import java.util.Objects;
+
+/**
+ * Class that will hold an instance of {@link DeviceConfigServiceManager}
+ * which is used by {@link DeviceConfig} to retrieve an instance of the service.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class DeviceConfigInitializer {
+    private static DeviceConfigServiceManager sDeviceConfigServiceManager;
+
+    private static final Object sLock = new Object();
+
+    private DeviceConfigInitializer() {
+        // fully static class
+    }
+
+    /**
+     * Setter for {@link DeviceConfigServiceManager}. Should be called only once.
+     *
+     */
+    public static void setDeviceConfigServiceManager(
+            @NonNull DeviceConfigServiceManager serviceManager) {
+        synchronized (sLock) {
+            if (sDeviceConfigServiceManager != null) {
+                throw new IllegalStateException("setDeviceConfigServiceManager called twice!");
+            }
+            Objects.requireNonNull(serviceManager, "serviceManager must not be null");
+
+            sDeviceConfigServiceManager = serviceManager;
+        }
+    }
+
+    /**
+     * Getter for {@link DeviceConfigServiceManager}.
+     *
+     */
+    @Nullable
+    public static DeviceConfigServiceManager getDeviceConfigServiceManager() {
+        synchronized (sLock) {
+            return sDeviceConfigServiceManager;
+        }
+    }
+}
diff --git a/core/java/android/provider/DeviceConfigServiceManager.java b/core/java/android/provider/DeviceConfigServiceManager.java
new file mode 100644
index 0000000..c362c37
--- /dev/null
+++ b/core/java/android/provider/DeviceConfigServiceManager.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2023 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.provider;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.IBinder;
+import android.os.ServiceManager;
+
+/**
+ * Service Manager for the {@code android.provider.DeviceConfig} service.
+ *
+ * <p>Used to be able to get an instance of the service in places that don't have access to a
+ * {@code Context}
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public class DeviceConfigServiceManager {
+
+    /**
+     * @hide
+     */
+    public DeviceConfigServiceManager() {
+    }
+
+    /**
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static final class ServiceRegisterer {
+        private final String mServiceName;
+
+        /**
+         * @hide
+         */
+        public ServiceRegisterer(String serviceName) {
+            mServiceName = serviceName;
+        }
+
+        /**
+         * Register a system server binding object for a service.
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        public void register(@NonNull IBinder service) {
+            ServiceManager.addService(mServiceName, service);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it returns null.
+         *
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @Nullable
+        public IBinder get() {
+            return ServiceManager.getService(mServiceName);
+        }
+
+        /**
+         * Get the system server binding object for a service.
+         *
+         * <p>This blocks until the service instance is ready,
+         * or a timeout happens, in which case it throws {@link ServiceNotFoundException}.
+         *
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @NonNull
+        public IBinder getOrThrow() throws ServiceNotFoundException {
+            try {
+                return ServiceManager.getServiceOrThrow(mServiceName);
+            } catch (ServiceManager.ServiceNotFoundException e) {
+                throw new ServiceNotFoundException(mServiceName);
+            }
+        }
+
+        /**
+         * Get the system server binding object for a service. If the specified service is
+         * not available, it returns null.
+         *
+         * @hide
+         */
+        @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+        @Nullable
+        public IBinder tryGet() {
+            return ServiceManager.checkService(mServiceName);
+        }
+
+    }
+
+    /**
+     * See {@link ServiceRegisterer#getOrThrow}.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    public static class ServiceNotFoundException extends ServiceManager.ServiceNotFoundException {
+        /**
+         * Constructor.
+         *
+         * @param name the name of the binder service that cannot be found.
+         *
+         * @hide
+         */
+        public ServiceNotFoundException(@NonNull String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Returns {@link ServiceRegisterer} for the device config service that
+     * is updatable via mainline.
+     *
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @NonNull
+    public ServiceRegisterer getDeviceConfigUpdatableServiceRegisterer() {
+        return new ServiceRegisterer("device_config_updatable");
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e0c022f..563adf8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1716,7 +1716,6 @@
      * Input: Nothing.
      * <p>
      * Output: Nothing
-     * @see android.nfc.NfcAdapter#isNdefPushEnabled()
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_NFCSHARING_SETTINGS =
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index cdcd6591..a8fcf86 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -166,7 +166,7 @@
                     PixelFormat.TRANSPARENT);
 
             final SurfaceControlViewHost host = new SurfaceControlViewHost(this, getDisplay(),
-                    hostInputToken);
+                    hostInputToken, "InlineSuggestionRenderService");
             host.setView(suggestionRoot, lp);
 
             // Set the suggestion view to be non-focusable so that if its background is set to a
diff --git a/core/java/android/service/games/GameSessionService.java b/core/java/android/service/games/GameSessionService.java
index 52c8ec3..f844423 100644
--- a/core/java/android/service/games/GameSessionService.java
+++ b/core/java/android/service/games/GameSessionService.java
@@ -125,7 +125,7 @@
         final Context windowContext = createWindowContext(display,
                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, /*options=*/ null);
         SurfaceControlViewHost surfaceControlViewHost =
-                new SurfaceControlViewHost(windowContext, display, hostToken);
+                new SurfaceControlViewHost(windowContext, display, hostToken, "GameSessionService");
 
         gameSession.attach(
                 gameSessionController,
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 3807685..9696dbc 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -51,8 +51,17 @@
 
     /** @hide */
     @StringDef (prefix = { "KEY_" }, value = {
-            KEY_CONTEXTUAL_ACTIONS, KEY_GROUP_KEY, KEY_IMPORTANCE, KEY_PEOPLE, KEY_SNOOZE_CRITERIA,
-            KEY_TEXT_REPLIES, KEY_USER_SENTIMENT, KEY_IMPORTANCE_PROPOSAL, KEY_SENSITIVE_CONTENT
+            KEY_PEOPLE,
+            KEY_SNOOZE_CRITERIA,
+            KEY_GROUP_KEY,
+            KEY_USER_SENTIMENT,
+            KEY_CONTEXTUAL_ACTIONS,
+            KEY_TEXT_REPLIES,
+            KEY_IMPORTANCE,
+            KEY_IMPORTANCE_PROPOSAL,
+            KEY_SENSITIVE_CONTENT,
+            KEY_RANKING_SCORE,
+            KEY_NOT_CONVERSATION
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Keys {}
@@ -65,6 +74,7 @@
      */
     @SystemApi
     public static final String KEY_PEOPLE = "key_people";
+
     /**
      * Parcelable {@code ArrayList} of {@link SnoozeCriterion}. These criteria may be visible to
      * users. If a user chooses to snooze a notification until one of these criterion, the
@@ -72,6 +82,7 @@
      * {@link NotificationAssistantService#onNotificationSnoozedUntilContext}.
      */
     public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+
     /**
      * Data type: String. Used to change what {@link Notification#getGroup() group} a notification
      * belongs to.
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index b384b66..37a91e7 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -58,6 +58,7 @@
     void onSuggestedReplySent(String key, in CharSequence reply, int source);
     void onActionClicked(String key, in Notification.Action action, int source);
     void onNotificationClicked(String key);
+    // @deprecated changing allowed adjustments is no longer supported.
     void onAllowedAdjustmentsChanged();
     void onNotificationFeedbackReceived(String key, in NotificationRankingUpdate update, in Bundle feedback);
 }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index a38ef96..76889df 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -293,7 +293,10 @@
      * their notifications the assistant can modify.
      * <p> Query {@link NotificationManager#getAllowedAssistantAdjustments()} to see what
      * {@link Adjustment adjustments} you are currently allowed to make.</p>
+     *
+     * @deprecated changing allowed adjustments is no longer supported.
      */
+    @Deprecated
     public void onAllowedAdjustmentsChanged() {
     }
 
diff --git a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
index 9292e96..59e3a5e 100644
--- a/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
+++ b/core/java/android/service/selectiontoolbar/RemoteSelectionToolbar.java
@@ -275,7 +275,7 @@
                     mHostInputToken, mTransferTouchListener);
             contentHolder.addView(mContentContainer);
             mSurfaceControlViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
-                    mHostInputToken);
+                    mHostInputToken, "RemoteSelectionToolbar");
             mSurfaceControlViewHost.setView(contentHolder, mPopupWidth, mPopupHeight);
         }
         if (mSurfacePackage == null) {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 23513fad..120e871 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -45,7 +45,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
-import android.content.res.TypedArray;
 import android.graphics.BLASTBufferQueue;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
@@ -1153,11 +1152,6 @@
                     mLayout.token = mWindowToken;
 
                     if (!mCreated) {
-                        // Retrieve watch round info
-                        TypedArray windowStyle = obtainStyledAttributes(
-                                com.android.internal.R.styleable.Window);
-                        windowStyle.recycle();
-
                         // Add window
                         mLayout.type = mIWallpaperEngine.mWindowType;
                         mLayout.gravity = Gravity.START|Gravity.TOP;
diff --git a/core/java/android/nfc/BeamShareData.aidl b/core/java/android/speech/RecognitionPart.aidl
similarity index 83%
rename from core/java/android/nfc/BeamShareData.aidl
rename to core/java/android/speech/RecognitionPart.aidl
index a47e240..f7754a2 100644
--- a/core/java/android/nfc/BeamShareData.aidl
+++ b/core/java/android/speech/RecognitionPart.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.nfc;
+package android.speech;
 
-parcelable BeamShareData;
+parcelable RecognitionPart;
diff --git a/core/java/android/speech/RecognitionPart.java b/core/java/android/speech/RecognitionPart.java
new file mode 100644
index 0000000..e551cdc
--- /dev/null
+++ b/core/java/android/speech/RecognitionPart.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2023 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.speech;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Info about a single recognition part.
+ *
+ * <p> A recognition part represents a recognized word or character, as well as any potential
+ * adjacent punctuation, that is returned by the {@link SpeechRecognizer}.
+ *
+ * <p> Each recognition part is described with a {@link String} denoting the raw text.
+ * Additionally, if formatting is enabled with {@link RecognizerIntent#EXTRA_ENABLE_FORMATTING},
+ * another {@link String} representation exists denoting the formatted text.
+ *
+ * <p> If the timestamps are requested with {@link RecognizerIntent#EXTRA_REQUEST_WORD_TIMING}, each
+ * recognition part will contain a value representing the offset of the beginning of this part from
+ * the start of the recognition session in milliseconds.
+ *
+ * <p> If the confidence levels are requested with
+ * {@link RecognizerIntent#EXTRA_REQUEST_WORD_CONFIDENCE}, each recognition part will contain
+ * a value describing the level of recognition confidence.
+ */
+@DataClass(
+        genBuilder = true,
+        genEqualsHashCode = true,
+        genHiddenConstDefs = true,
+        genToString = true)
+public final class RecognitionPart implements Parcelable {
+
+    /** Confidence level not requested. */
+    public static final int CONFIDENCE_LEVEL_UNKNOWN = 0;
+
+    /** Lowest level of confidence out of five levels. */
+    public static final int CONFIDENCE_LEVEL_LOW = 1;
+
+    /** Second-lowest level of confidence out of five levels. */
+    public static final int CONFIDENCE_LEVEL_LOW_MEDIUM = 2;
+
+    /** Medium level of confidence out of five levels. */
+    public static final int CONFIDENCE_LEVEL_MEDIUM = 3;
+
+    /** Second-highest level of confidence out of five levels. */
+    public static final int CONFIDENCE_LEVEL_MEDIUM_HIGH = 4;
+
+    /** Highest level of confidence out of five levels. */
+    public static final int CONFIDENCE_LEVEL_HIGH = 5;
+
+    /** The {@code non-null} raw text version of the recognized part of the result. */
+    @NonNull
+    private final String mRawText;
+
+    /**
+     * The formatted text version of the recognized part of the result. If formatting is enabled
+     * with {@link RecognizerIntent#EXTRA_ENABLE_FORMATTING}, it has a {@code non-null} value.
+     *
+     * <p> Otherwise, it should be {@code null} by default.
+     */
+    @Nullable
+    private final String mFormattedText;
+    private static String defaultFormattedText() {
+        return null;
+    }
+
+    /**
+     * Non-negative offset of the beginning of this part from
+     * the start of the recognition session in milliseconds
+     * if requested with {@link RecognizerIntent#EXTRA_REQUEST_WORD_TIMING}.
+     *
+     * <p> Otherwise, this should equal 0.
+     */
+    private final long mTimestampMillis;
+    private static long defaultTimestampMillis() {
+        return 0;
+    }
+
+    /**
+     * The level of confidence for this part if requested
+     * with {@link RecognizerIntent#EXTRA_REQUEST_WORD_CONFIDENCE}.
+     *
+     * <p> Otherwise, this should equal {@link #CONFIDENCE_LEVEL_UNKNOWN}.
+     */
+    @ConfidenceLevel
+    private final int mConfidenceLevel;
+    @ConfidenceLevel
+    private static int defaultConfidenceLevel() {
+        return CONFIDENCE_LEVEL_UNKNOWN;
+    }
+
+    private void onConstructed() {
+        Preconditions.checkArgumentNonnegative(mTimestampMillis,
+                "The timestamp must be non-negative.");
+    }
+
+    @DataClass.Suppress("setFormattedText")
+    abstract static class BaseBuilder {
+        /**
+         * The formatted text version of the recognized part of the result. If formatting is enabled
+         * with {@link RecognizerIntent#EXTRA_ENABLE_FORMATTING}, it has a {@code non-null} value.
+         *
+         * <p> Otherwise, it should be {@code null} by default.
+         */
+        @NonNull
+        public Builder setFormattedText(@NonNull String value) {
+            // Method explicitly defined, so that the argument can be checked for non-null.
+            com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, value);
+
+            final Builder builder = (Builder) this;
+            builder.checkNotUsed();
+            builder.mBuilderFieldsSet |= 0x2;
+            builder.mFormattedText = value;
+            return builder;
+        }
+    }
+
+
+
+    // Code below generated by codegen v1.0.23.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/speech/RecognitionPart.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /** @hide */
+    @android.annotation.IntDef(prefix = "CONFIDENCE_LEVEL_", value = {
+        CONFIDENCE_LEVEL_UNKNOWN,
+        CONFIDENCE_LEVEL_LOW,
+        CONFIDENCE_LEVEL_LOW_MEDIUM,
+        CONFIDENCE_LEVEL_MEDIUM,
+        CONFIDENCE_LEVEL_MEDIUM_HIGH,
+        CONFIDENCE_LEVEL_HIGH
+    })
+    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE)
+    @DataClass.Generated.Member
+    public @interface ConfidenceLevel {}
+
+    /** @hide */
+    @DataClass.Generated.Member
+    public static String confidenceLevelToString(@ConfidenceLevel int value) {
+        switch (value) {
+            case CONFIDENCE_LEVEL_UNKNOWN:
+                    return "CONFIDENCE_LEVEL_UNKNOWN";
+            case CONFIDENCE_LEVEL_LOW:
+                    return "CONFIDENCE_LEVEL_LOW";
+            case CONFIDENCE_LEVEL_LOW_MEDIUM:
+                    return "CONFIDENCE_LEVEL_LOW_MEDIUM";
+            case CONFIDENCE_LEVEL_MEDIUM:
+                    return "CONFIDENCE_LEVEL_MEDIUM";
+            case CONFIDENCE_LEVEL_MEDIUM_HIGH:
+                    return "CONFIDENCE_LEVEL_MEDIUM_HIGH";
+            case CONFIDENCE_LEVEL_HIGH:
+                    return "CONFIDENCE_LEVEL_HIGH";
+            default: return Integer.toHexString(value);
+        }
+    }
+
+    @DataClass.Generated.Member
+    /* package-private */ RecognitionPart(
+            @NonNull String rawText,
+            @Nullable String formattedText,
+            long timestampMillis,
+            @ConfidenceLevel int confidenceLevel) {
+        this.mRawText = rawText;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mRawText);
+        this.mFormattedText = formattedText;
+        this.mTimestampMillis = timestampMillis;
+        this.mConfidenceLevel = confidenceLevel;
+
+        if (!(mConfidenceLevel == CONFIDENCE_LEVEL_UNKNOWN)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_LOW)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_LOW_MEDIUM)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_MEDIUM)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_MEDIUM_HIGH)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_HIGH)) {
+            throw new java.lang.IllegalArgumentException(
+                    "confidenceLevel was " + mConfidenceLevel + " but must be one of: "
+                            + "CONFIDENCE_LEVEL_UNKNOWN(" + CONFIDENCE_LEVEL_UNKNOWN + "), "
+                            + "CONFIDENCE_LEVEL_LOW(" + CONFIDENCE_LEVEL_LOW + "), "
+                            + "CONFIDENCE_LEVEL_LOW_MEDIUM(" + CONFIDENCE_LEVEL_LOW_MEDIUM + "), "
+                            + "CONFIDENCE_LEVEL_MEDIUM(" + CONFIDENCE_LEVEL_MEDIUM + "), "
+                            + "CONFIDENCE_LEVEL_MEDIUM_HIGH(" + CONFIDENCE_LEVEL_MEDIUM_HIGH + "), "
+                            + "CONFIDENCE_LEVEL_HIGH(" + CONFIDENCE_LEVEL_HIGH + ")");
+        }
+
+
+        onConstructed();
+    }
+
+    /**
+     * The {@code non-null} raw text version of the recognized part of the result.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getRawText() {
+        return mRawText;
+    }
+
+    /**
+     * The formatted text version of the recognized part of the result. If formatting is enabled
+     * with {@link RecognizerIntent#EXTRA_ENABLE_FORMATTING}, it has a {@code non-null} value.
+     *
+     * <p> Otherwise, it should be {@code null} by default.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getFormattedText() {
+        return mFormattedText;
+    }
+
+    /**
+     * Non-negative offset of the beginning of this part from
+     * the start of the recognition session in milliseconds
+     * if requested with {@link RecognizerIntent#EXTRA_REQUEST_WORD_TIMING}.
+     *
+     * <p> Otherwise, this should equal 0.
+     */
+    @DataClass.Generated.Member
+    public long getTimestampMillis() {
+        return mTimestampMillis;
+    }
+
+    /**
+     * The level of confidence for this part if requested
+     * with {@link RecognizerIntent#EXTRA_REQUEST_WORD_CONFIDENCE}.
+     *
+     * <p> Otherwise, this should equal {@link #CONFIDENCE_LEVEL_UNKNOWN}.
+     */
+    @DataClass.Generated.Member
+    public @ConfidenceLevel int getConfidenceLevel() {
+        return mConfidenceLevel;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public String toString() {
+        // You can override field toString logic by defining methods like:
+        // String fieldNameToString() { ... }
+
+        return "RecognitionPart { " +
+                "rawText = " + mRawText + ", " +
+                "formattedText = " + mFormattedText + ", " +
+                "timestampMillis = " + mTimestampMillis + ", " +
+                "confidenceLevel = " + confidenceLevelToString(mConfidenceLevel) +
+        " }";
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(RecognitionPart other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        RecognitionPart that = (RecognitionPart) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && java.util.Objects.equals(mRawText, that.mRawText)
+                && java.util.Objects.equals(mFormattedText, that.mFormattedText)
+                && mTimestampMillis == that.mTimestampMillis
+                && mConfidenceLevel == that.mConfidenceLevel;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mRawText);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mFormattedText);
+        _hash = 31 * _hash + Long.hashCode(mTimestampMillis);
+        _hash = 31 * _hash + mConfidenceLevel;
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mFormattedText != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeString(mRawText);
+        if (mFormattedText != null) dest.writeString(mFormattedText);
+        dest.writeLong(mTimestampMillis);
+        dest.writeInt(mConfidenceLevel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RecognitionPart(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String rawText = in.readString();
+        String formattedText = (flg & 0x2) == 0 ? null : in.readString();
+        long timestampMillis = in.readLong();
+        int confidenceLevel = in.readInt();
+
+        this.mRawText = rawText;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mRawText);
+        this.mFormattedText = formattedText;
+        this.mTimestampMillis = timestampMillis;
+        this.mConfidenceLevel = confidenceLevel;
+
+        if (!(mConfidenceLevel == CONFIDENCE_LEVEL_UNKNOWN)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_LOW)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_LOW_MEDIUM)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_MEDIUM)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_MEDIUM_HIGH)
+                && !(mConfidenceLevel == CONFIDENCE_LEVEL_HIGH)) {
+            throw new java.lang.IllegalArgumentException(
+                    "confidenceLevel was " + mConfidenceLevel + " but must be one of: "
+                            + "CONFIDENCE_LEVEL_UNKNOWN(" + CONFIDENCE_LEVEL_UNKNOWN + "), "
+                            + "CONFIDENCE_LEVEL_LOW(" + CONFIDENCE_LEVEL_LOW + "), "
+                            + "CONFIDENCE_LEVEL_LOW_MEDIUM(" + CONFIDENCE_LEVEL_LOW_MEDIUM + "), "
+                            + "CONFIDENCE_LEVEL_MEDIUM(" + CONFIDENCE_LEVEL_MEDIUM + "), "
+                            + "CONFIDENCE_LEVEL_MEDIUM_HIGH(" + CONFIDENCE_LEVEL_MEDIUM_HIGH + "), "
+                            + "CONFIDENCE_LEVEL_HIGH(" + CONFIDENCE_LEVEL_HIGH + ")");
+        }
+
+
+        onConstructed();
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RecognitionPart> CREATOR
+            = new Parcelable.Creator<RecognitionPart>() {
+        @Override
+        public RecognitionPart[] newArray(int size) {
+            return new RecognitionPart[size];
+        }
+
+        @Override
+        public RecognitionPart createFromParcel(@NonNull android.os.Parcel in) {
+            return new RecognitionPart(in);
+        }
+    };
+
+    /**
+     * A builder for {@link RecognitionPart}
+     */
+    @SuppressWarnings("WeakerAccess")
+    @DataClass.Generated.Member
+    public static final class Builder extends BaseBuilder {
+
+        private @NonNull String mRawText;
+        private @Nullable String mFormattedText;
+        private long mTimestampMillis;
+        private @ConfidenceLevel int mConfidenceLevel;
+
+        private long mBuilderFieldsSet = 0L;
+
+        /**
+         * Creates a new Builder.
+         *
+         * @param rawText
+         *   The {@code non-null} raw text version of the recognized part of the result.
+         */
+        public Builder(
+                @NonNull String rawText) {
+            mRawText = rawText;
+            com.android.internal.util.AnnotationValidations.validate(
+                    NonNull.class, null, mRawText);
+        }
+
+        /**
+         * The {@code non-null} raw text version of the recognized part of the result.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setRawText(@NonNull String value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mRawText = value;
+            return this;
+        }
+
+        /**
+         * Non-negative offset of the beginning of this part from
+         * the start of the recognition session in milliseconds
+         * if requested with {@link RecognizerIntent#EXTRA_REQUEST_WORD_TIMING}.
+         *
+         * <p> Otherwise, this should equal 0.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTimestampMillis(long value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mTimestampMillis = value;
+            return this;
+        }
+
+        /**
+         * The level of confidence for this part if requested
+         * with {@link RecognizerIntent#EXTRA_REQUEST_WORD_CONFIDENCE}.
+         *
+         * <p> Otherwise, this should equal {@link #CONFIDENCE_LEVEL_UNKNOWN}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setConfidenceLevel(@ConfidenceLevel int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mConfidenceLevel = value;
+            return this;
+        }
+
+        /** Builds the instance. This builder should not be touched after calling this! */
+        public @NonNull RecognitionPart build() {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x10; // Mark builder used
+
+            if ((mBuilderFieldsSet & 0x2) == 0) {
+                mFormattedText = defaultFormattedText();
+            }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mTimestampMillis = defaultTimestampMillis();
+            }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mConfidenceLevel = defaultConfidenceLevel();
+            }
+            RecognitionPart o = new RecognitionPart(
+                    mRawText,
+                    mFormattedText,
+                    mTimestampMillis,
+                    mConfidenceLevel);
+            return o;
+        }
+
+        private void checkNotUsed() {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
+                throw new IllegalStateException(
+                        "This Builder should not be reused. Use a new Builder instance instead");
+            }
+        }
+    }
+
+    @DataClass.Generated(
+            time = 1676294616910L,
+            codegenVersion = "1.0.23",
+            sourceFile = "frameworks/base/core/java/android/speech/RecognitionPart.java",
+            inputSignatures = "public static final  int CONFIDENCE_LEVEL_UNKNOWN\npublic static final  int CONFIDENCE_LEVEL_LOW\npublic static final  int CONFIDENCE_LEVEL_LOW_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM\npublic static final  int CONFIDENCE_LEVEL_MEDIUM_HIGH\npublic static final  int CONFIDENCE_LEVEL_HIGH\nprivate final @android.annotation.NonNull java.lang.String mRawText\nprivate final @android.annotation.Nullable java.lang.String mFormattedText\nprivate final  long mTimestampMillis\nprivate final @android.speech.RecognitionPart.ConfidenceLevel int mConfidenceLevel\nprivate static  java.lang.String defaultFormattedText()\nprivate static  long defaultTimestampMillis()\nprivate static @android.speech.RecognitionPart.ConfidenceLevel int defaultConfidenceLevel()\nprivate  void onConstructed()\nclass RecognitionPart extends java.lang.Object implements [android.os.Parcelable]\npublic @android.annotation.NonNull android.speech.RecognitionPart.Builder setFormattedText(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genToString=true)\npublic @android.annotation.NonNull android.speech.RecognitionPart.Builder setFormattedText(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 1cc772a..a5dbdd7 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -142,6 +142,10 @@
                 if (preflightPermissionCheckPassed) {
                     currentCallback = new Callback(listener, attributionSource);
                     sessionState = new SessionState(currentCallback);
+                    mSessions.put(listener.asBinder(), sessionState);
+                    if (DBG) {
+                        Log.d(TAG, "Added a new session to the map, pending permission checks");
+                    }
                     RecognitionService.this.onStartListening(intent, currentCallback);
                 }
 
@@ -151,16 +155,12 @@
                     if (preflightPermissionCheckPassed) {
                         // If start listening was attempted, cancel the callback.
                         RecognitionService.this.onCancel(currentCallback);
+                        mSessions.remove(listener.asBinder());
                         finishDataDelivery(sessionState);
                         sessionState.reset();
                     }
                     Log.i(TAG, "#startListening received from a caller "
                             + "without permission " + Manifest.permission.RECORD_AUDIO + ".");
-                } else {
-                    if (DBG) {
-                        Log.d(TAG, "Added a new session to the map.");
-                    }
-                    mSessions.put(listener.asBinder(), sessionState);
                 }
             } else {
                 listener.onError(SpeechRecognizer.ERROR_CLIENT);
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index cd18c19..7127fa1 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -558,4 +558,18 @@
      * @see #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS
      */
     public static final String EXTRA_SEGMENTED_SESSION = "android.speech.extra.SEGMENTED_SESSION";
+
+    /**
+     * Optional boolean indicating whether the recognizer should return the timestamp
+     * of each word in the final recognition results.
+     */
+    public static final String EXTRA_REQUEST_WORD_TIMING =
+            "android.speech.extra.REQUEST_WORD_TIMING";
+
+    /**
+     * Optional boolean indicating whether the recognizer should return the confidence
+     * level of each word in the final recognition results.
+     */
+    public static final String EXTRA_REQUEST_WORD_CONFIDENCE =
+            "android.speech.extra.REQUEST_WORD_CONFIDENCE";
 }
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index bcb8005..33c5b5a 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -113,6 +113,17 @@
     public static final String RESULTS_ALTERNATIVES = "results_alternatives";
 
     /**
+     * Key used to receive an ArrayList&lt;{@link RecognitionPart}&gt; object from the
+     * {@link Bundle} passed to the {@link RecognitionListener#onResults(Bundle)} and
+     * {@link RecognitionListener#onSegmentResults(Bundle)} methods.
+     *
+     * <p> A single {@link SpeechRecognizer} result is represented as a {@link String}. Each word of
+     * the resulting String, as well as any potential adjacent punctuation, is represented by a
+     * {@link RecognitionPart} item from the ArrayList retrieved by this key.
+     */
+    public static final String RECOGNITION_PARTS = "recognition_parts";
+
+    /**
      * The reason speech recognition failed.
      *
      * @hide
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index af43222..98c0d7f 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -129,7 +129,8 @@
      *
      * @hide
      */
-    public static final class TimeResult {
+    // Non-final for mocking frameworks
+    public static class TimeResult {
         private final long mUnixEpochTimeMillis;
         private final long mElapsedRealtimeMillis;
         private final int mUncertaintyMillis;
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index 6f8c5db..ea7efc7 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -184,24 +184,15 @@
     }
 
     /**
-     * @see #computeSha256DigestForLargeFile(String, byte[], String)
-     */
-    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
-            @NonNull byte[] fileBuffer) {
-        return computeSha256DigestForLargeFile(filePath, fileBuffer, null);
-    }
-
-    /**
-     * Computes the SHA256 digest of large files. This is typically useful for large APEXs.
+     * Computes the SHA256 digest of a large file.
      * @param filePath The path to which the file's content is to be hashed.
      * @param fileBuffer A buffer to read file's content into memory. It is strongly recommended to
      *                   make use of the {@link #createLargeFileBuffer()} method to create this
      *                   buffer.
-     * @param separator Separator between each pair of characters, such as colon, or null to omit.
-     * @return The SHA256 digest or null if an error occurs.
+     * @return The byte array of SHA256 digest or null if an error occurs.
      */
-    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
-            @NonNull byte[] fileBuffer, @Nullable String separator) {
+    public static @Nullable byte[] computeSha256DigestForLargeFileAsBytes(@NonNull String filePath,
+            @NonNull byte[] fileBuffer) {
         MessageDigest messageDigest;
         try {
             messageDigest = MessageDigest.getInstance("SHA256");
@@ -221,8 +212,30 @@
             return null;
         }
 
-        byte[] resultBytes = messageDigest.digest();
+        return messageDigest.digest();
+    }
 
+    /**
+     * @see #computeSha256DigestForLargeFile(String, byte[], String)
+     */
+    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
+            @NonNull byte[] fileBuffer) {
+        return computeSha256DigestForLargeFile(filePath, fileBuffer, null);
+    }
+
+    /**
+     * Computes the SHA256 digest of a large file.
+     * @param filePath The path to which the file's content is to be hashed.
+     * @param fileBuffer A buffer to read file's content into memory. It is strongly recommended to
+     *                   make use of the {@link #createLargeFileBuffer()} method to create this
+     *                   buffer.
+     * @param separator Separator between each pair of characters, such as colon, or null to omit.
+     * @see #computeSha256DigestForLargeFile(String, byte[])
+     * @return The encoded string of SHA256 digest or null if an error occurs.
+     */
+    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
+            @NonNull byte[] fileBuffer, @Nullable String separator) {
+        byte[] resultBytes = computeSha256DigestForLargeFileAsBytes(filePath, fileBuffer);
         if (separator == null) {
             return HexEncoding.encodeToString(resultBytes, false);
         }
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 3dc79cf..d0acedb 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -367,6 +367,14 @@
     }
 
     /**
+     * Check if the sourced looper and the current looper are same.
+     * @hide
+     */
+    boolean isTheLooperSame(Looper looper) {
+        return mLooper == looper;
+    }
+
+    /**
      * The amount of time, in milliseconds, between each frame of the animation.
      * <p>
      * This is a requested time that the animation will attempt to honor, but the actual delay
diff --git a/core/java/android/view/DragEvent.java b/core/java/android/view/DragEvent.java
index bda707b..1898407 100644
--- a/core/java/android/view/DragEvent.java
+++ b/core/java/android/view/DragEvent.java
@@ -619,10 +619,11 @@
             }
             if (in.readInt() != 0) {
                 event.mDragSurface = SurfaceControl.CREATOR.createFromParcel(in);
+                event.mDragSurface.setUnreleasedWarningCallSite("DragEvent");
             }
             if (in.readInt() != 0) {
                 event.mDragAndDropPermissions =
-                        IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());;
+                        IDragAndDropPermissions.Stub.asInterface(in.readStrongBinder());
             }
             return event;
         }
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 8d8ddb9..5ea640c 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -296,6 +296,9 @@
         taskId = in.readInt();
         mode = in.readInt();
         leash = in.readTypedObject(SurfaceControl.CREATOR);
+        if (leash != null) {
+            leash.setUnreleasedWarningCallSite("RemoteAnimationTarget[leash]");
+        }
         isTranslucent = in.readBoolean();
         clipRect = in.readTypedObject(Rect.CREATOR);
         contentInsets = in.readTypedObject(Rect.CREATOR);
@@ -307,6 +310,9 @@
         windowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
         isNotInRecents = in.readBoolean();
         startLeash = in.readTypedObject(SurfaceControl.CREATOR);
+        if (startLeash != null) {
+            startLeash.setUnreleasedWarningCallSite("RemoteAnimationTarget[startLeash]");
+        }
         startBounds = in.readTypedObject(Rect.CREATOR);
         taskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
         allowEnterPip = in.readBoolean();
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 8e09411..85365b3 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -1291,6 +1291,20 @@
     }
 
     /**
+     * Provides more information to show about the source of this SurfaceControl if it is finalized
+     * without being released. This is primarily intended for callers to update the call site after
+     * receiving a SurfaceControl from another process, which would otherwise get a generic default
+     * call site.
+     * @hide
+     */
+    public void setUnreleasedWarningCallSite(@NonNull String callsite) {
+        if (!isValid()) {
+            return;
+        }
+        mCloseGuard.openWithCallSite("release", callsite);
+    }
+
+    /**
      * Checks whether two {@link SurfaceControl} objects represent the same surface.
      *
      * @param other The other object to check
@@ -1303,27 +1317,36 @@
     }
 
     /**
-     * Returns the associated {@link Choreographer} instance with the
-     * current instance of the SurfaceControl.
-     * Must be called from a thread that already has a {@link android.os.Looper}
-     * associated with it.
-     * If there is no {@link Choreographer} associated with the SurfaceControl then a new instance
-     * of the {@link Choreographer} is created.
+     * When called for the first time a new instance of the {@link Choreographer} is created
+     * with a {@link android.os.Looper} of the current thread. Every subsequent call will return
+     * the same instance of the Choreographer.
+     *
+     * @see #getChoreographer(Looper) to create Choreographer with a different
+     * looper than current thread looper.
      *
      * @hide
      */
     @TestApi
     public @NonNull Choreographer getChoreographer() {
-        return getChoreographer(Looper.myLooper());
+        checkNotReleased();
+        synchronized (mChoreographerLock) {
+            if (mChoreographer == null) {
+                return getChoreographer(Looper.myLooper());
+            }
+            return mChoreographer;
+        }
     }
 
     /**
-     * Returns the associated {@link Choreographer} instance with the
-     * current instance of the SurfaceControl.
-     * If there is no {@link Choreographer} associated with the SurfaceControl then a new instance
-     * of the {@link Choreographer} is created.
+     * When called for the first time a new instance of the {@link Choreographer} is created with
+     * the sourced {@link android.os.Looper}. Every subsequent call will return the same
+     * instance of the Choreographer.
      *
-     * @param looper the choreographer is attached on this looper
+     * @see #getChoreographer()
+     *
+     * @throws IllegalStateException when a {@link Choreographer} instance exists with a different
+     * looper than sourced.
+     * @param looper the choreographer is attached on this looper.
      *
      * @hide
      */
@@ -1331,11 +1354,12 @@
     public @NonNull Choreographer getChoreographer(@NonNull Looper looper) {
         checkNotReleased();
         synchronized (mChoreographerLock) {
-            if (mChoreographer != null) {
-                return mChoreographer;
+            if (mChoreographer == null) {
+                mChoreographer = Choreographer.getInstanceForSurfaceControl(mNativeHandle, looper);
+            } else if (!mChoreographer.isTheLooperSame(looper)) {
+                throw new IllegalStateException(
+                        "Choreographer already exists with a different looper");
             }
-
-            mChoreographer = Choreographer.getInstanceForSurfaceControl(mNativeHandle, looper);
             return mChoreographer;
         }
     }
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index f7bdd09..ac50d09 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -32,6 +32,8 @@
 import android.window.ISurfaceSyncGroup;
 import android.window.WindowTokenClient;
 
+import dalvik.system.CloseGuard;
+
 import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -51,6 +53,7 @@
 public class SurfaceControlViewHost {
     private final static String TAG = "SurfaceControlViewHost";
     private final ViewRootImpl mViewRoot;
+    private final CloseGuard mCloseGuard = CloseGuard.get();
     private WindowlessWindowManager mWm;
 
     private SurfaceControl mSurfaceControl;
@@ -170,6 +173,7 @@
         private SurfacePackage(Parcel in) {
             mSurfaceControl = new SurfaceControl();
             mSurfaceControl.readFromParcel(in);
+            mSurfaceControl.setUnreleasedWarningCallSite("SurfacePackage(Parcel)");
             mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
                     in.readStrongBinder());
             mInputToken = in.readStrongBinder();
@@ -291,9 +295,10 @@
 
     /** @hide */
     public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d,
-            @NonNull WindowlessWindowManager wwm) {
+            @NonNull WindowlessWindowManager wwm, @NonNull String callsite) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
+        mCloseGuard.openWithCallSite("release", callsite);
         addConfigCallback(c, d);
 
         WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -315,15 +320,35 @@
      */
     public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display,
             @Nullable IBinder hostToken) {
+        this(context, display, hostToken, "untracked");
+    }
+
+    /**
+     * Construct a new SurfaceControlViewHost. The root Surface will be
+     * allocated internally and is accessible via getSurfacePackage().
+     *
+     * The {@param hostToken} parameter, primarily used for ANR reporting,
+     * must be obtained from whomever will be hosting the embedded hierarchy.
+     * It's accessible from {@link SurfaceView#getHostToken}.
+     *
+     * @param context The Context object for your activity or application.
+     * @param display The Display the hierarchy will be placed on.
+     * @param hostToken The host token, as discussed above.
+     * @param callsite The call site, used for tracking leakage of the host
+     * @hide
+     */
+    public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display,
+            @Nullable IBinder hostToken, @NonNull String callsite) {
         mSurfaceControl = new SurfaceControl.Builder()
                 .setContainerLayer()
                 .setName("SurfaceControlViewHost")
-                .setCallsite("SurfaceControlViewHost")
+                .setCallsite("SurfaceControlViewHost[" + callsite + "]")
                 .build();
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
 
         mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
+        mCloseGuard.openWithCallSite("release", callsite);
         addConfigCallback(context, display);
 
         WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -349,7 +374,9 @@
         if (mReleased) {
             return;
         }
-        Log.e(TAG, "SurfaceControlViewHost finalized without being released: " + this);
+        if (mCloseGuard != null) {
+            mCloseGuard.warnIfOpen();
+        }
         // We aren't on the UI thread here so we need to pass false to doDie
         mViewRoot.die(false /* immediate */);
         WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
@@ -465,6 +492,7 @@
         mViewRoot.die(true /* immediate */);
         WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
         mReleased = true;
+        mCloseGuard.close();
     }
 
     /**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cecfc8a..241ee5a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8945,11 +8945,33 @@
         outRect.set(position.left, position.top, position.right, position.bottom);
     }
 
+    /**
+     * Gets the location of this view in window coordinates.
+     *
+     * @param outRect The output location
+     * @param clipToParent Whether to clip child bounds to the parent ones.
+     * @hide
+     */
+    public void getBoundsInWindow(Rect outRect, boolean clipToParent) {
+        if (mAttachInfo == null) {
+            return;
+        }
+        RectF position = mAttachInfo.mTmpTransformRect;
+        getBoundsToWindowInternal(position, clipToParent);
+        outRect.set(Math.round(position.left), Math.round(position.top),
+                Math.round(position.right), Math.round(position.bottom));
+    }
+
     private void getBoundsToScreenInternal(RectF position, boolean clipToParent) {
         position.set(0, 0, mRight - mLeft, mBottom - mTop);
         mapRectFromViewToScreenCoords(position, clipToParent);
     }
 
+    private void getBoundsToWindowInternal(RectF position, boolean clipToParent) {
+        position.set(0, 0, mRight - mLeft, mBottom - mTop);
+        mapRectFromViewToWindowCoords(position, clipToParent);
+    }
+
     /**
      * Map a rectangle from view-relative coordinates to screen-relative coordinates
      *
@@ -8958,6 +8980,18 @@
      * @hide
      */
     public void mapRectFromViewToScreenCoords(RectF rect, boolean clipToParent) {
+        mapRectFromViewToWindowCoords(rect, clipToParent);
+        rect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
+    }
+
+    /**
+     * Map a rectangle from view-relative coordinates to window-relative coordinates
+     *
+     * @param rect The rectangle to be mapped
+     * @param clipToParent Whether to clip child bounds to the parent ones.
+     * @hide
+     */
+    public void mapRectFromViewToWindowCoords(RectF rect, boolean clipToParent) {
         if (!hasIdentityMatrix()) {
             getMatrix().mapRect(rect);
         }
@@ -8990,8 +9024,6 @@
             ViewRootImpl viewRootImpl = (ViewRootImpl) parent;
             rect.offset(0, -viewRootImpl.mCurScrollY);
         }
-
-        rect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
     }
 
     /**
@@ -10578,6 +10610,8 @@
 
         getBoundsOnScreen(bounds, true);
         info.setBoundsInScreen(bounds);
+        getBoundsInWindow(bounds, true);
+        info.setBoundsInWindow(bounds);
 
         ViewParent parent = getParentForAccessibility();
         if (parent instanceof View) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index d88cdf1..4576d62 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -906,6 +906,7 @@
     private int mBooleanProperties;
     private final Rect mBoundsInParent = new Rect();
     private final Rect mBoundsInScreen = new Rect();
+    private final Rect mBoundsInWindow = new Rect();
     private int mDrawingOrderInParent;
 
     private CharSequence mPackageName;
@@ -2156,6 +2157,49 @@
     }
 
     /**
+     * Gets the node bounds in window coordinates.
+     * <p>
+     * When magnification is enabled, the bounds in window are scaled up by magnification scale
+     * and the positions are also adjusted according to the offset of magnification viewport.
+     * For example, it returns Rect(-180, -180, 0, 0) for original bounds Rect(10, 10, 100, 100),
+     * when the magnification scale is 2 and offsets for X and Y are both 200.
+     * <p/>
+     *
+     * @param outBounds The output node bounds.
+     */
+    public void getBoundsInWindow(@NonNull Rect outBounds) {
+        outBounds.set(mBoundsInWindow.left, mBoundsInWindow.top,
+                mBoundsInWindow.right, mBoundsInWindow.bottom);
+    }
+
+    /**
+     * Returns the actual rect containing the node bounds in window coordinates.
+     *
+     * @hide Not safe to expose outside the framework.
+     */
+    @NonNull
+    public Rect getBoundsInWindow() {
+        return mBoundsInWindow;
+    }
+
+    /**
+     * Sets the node bounds in window coordinates.
+     * <p>
+     *   <strong>Note:</strong> Cannot be called from an
+     *   {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     *
+     * @param bounds The node bounds.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setBoundsInWindow(@NonNull Rect bounds) {
+        enforceNotSealed();
+        mBoundsInWindow.set(bounds);
+    }
+
+    /**
      * Gets whether this node is checkable.
      *
      * @return True if the node is checkable.
@@ -4054,6 +4098,11 @@
             nonDefaultFields |= bitAt(fieldIndex);
         }
         fieldIndex++;
+        if (!Objects.equals(mBoundsInWindow, DEFAULT.mBoundsInWindow)) {
+            nonDefaultFields |= bitAt(fieldIndex);
+        }
+        fieldIndex++;
+
         if (!Objects.equals(mActions, DEFAULT.mActions)) nonDefaultFields |= bitAt(fieldIndex);
         fieldIndex++;
         if (mMaxTextLength != DEFAULT.mMaxTextLength) nonDefaultFields |= bitAt(fieldIndex);
@@ -4203,6 +4252,13 @@
         }
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            parcel.writeInt(mBoundsInWindow.top);
+            parcel.writeInt(mBoundsInWindow.bottom);
+            parcel.writeInt(mBoundsInWindow.left);
+            parcel.writeInt(mBoundsInWindow.right);
+        }
+
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
             if (mActions != null && !mActions.isEmpty()) {
                 final int actionCount = mActions.size();
 
@@ -4333,6 +4389,7 @@
         mUniqueId = other.mUniqueId;
         mBoundsInParent.set(other.mBoundsInParent);
         mBoundsInScreen.set(other.mBoundsInScreen);
+        mBoundsInWindow.set(other.mBoundsInWindow);
         mPackageName = other.mPackageName;
         mClassName = other.mClassName;
         mText = other.mText;
@@ -4465,6 +4522,13 @@
         }
 
         if (isBitSet(nonDefaultFields, fieldIndex++)) {
+            mBoundsInWindow.top = parcel.readInt();
+            mBoundsInWindow.bottom = parcel.readInt();
+            mBoundsInWindow.left = parcel.readInt();
+            mBoundsInWindow.right = parcel.readInt();
+        }
+
+        if (isBitSet(nonDefaultFields, fieldIndex++)) {
             final long standardActions = parcel.readLong();
             addStandardActions(standardActions);
             final int nonStandardActionCount = parcel.readInt();
@@ -4815,6 +4879,7 @@
 
         builder.append("; boundsInParent: ").append(mBoundsInParent);
         builder.append("; boundsInScreen: ").append(mBoundsInScreen);
+        builder.append("; boundsInWindow: ").append(mBoundsInScreen);
 
         builder.append("; packageName: ").append(mPackageName);
         builder.append("; className: ").append(mClassName);
diff --git a/core/java/android/window/SplashScreenView.java b/core/java/android/window/SplashScreenView.java
index bc9f74e..bdaad2b 100644
--- a/core/java/android/window/SplashScreenView.java
+++ b/core/java/android/window/SplashScreenView.java
@@ -333,7 +333,8 @@
 
                 SurfaceControlViewHost viewHost = new SurfaceControlViewHost(viewContext,
                         viewContext.getDisplay(),
-                        surfaceView.getHostToken());
+                        surfaceView.getHostToken(),
+                        "SplashScreenView");
                 ImageView imageView = new ImageView(viewContext);
                 imageView.setBackground(mIconDrawable);
                 viewHost.setView(imageView, mIconSize, mIconSize);
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index c8a69e2..e277b49 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -198,6 +198,7 @@
         in.readTypedList(mChanges, Change.CREATOR);
         mRootLeash = new SurfaceControl();
         mRootLeash.readFromParcel(in);
+        mRootLeash.setUnreleasedWarningCallSite("TransitionInfo");
         mRootOffset.readFromParcel(in);
         mOptions = in.readTypedObject(AnimationOptions.CREATOR);
     }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 1084c71..f42bc81 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -48,25 +48,10 @@
     public static final String NAS_MAX_SUGGESTIONS = "nas_max_suggestions";
 
     /**
-     * Whether the Notification Assistant can change ranking.
-     */
-    public static final String ENABLE_NAS_RANKING = "enable_nas_ranking";
-
-    /**
-     * Whether the Notification Assistant can prioritize notification.
-     */
-    public static final String ENABLE_NAS_PRIORITIZER = "enable_nas_prioritizer";
-
-    /**
      * Whether to enable feedback UI for Notification Assistant
      */
     public static final String ENABLE_NAS_FEEDBACK = "enable_nas_feedback";
 
-    /**
-     * Whether the Notification Assistant can label a notification not a conversation
-     */
-    public static final String ENABLE_NAS_NOT_CONVERSATION = "enable_nas_not_conversation";
-
     // Flags related to screenshot intelligence
 
     /**
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b79c540..b4599c8 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -332,7 +332,6 @@
                 "libdl",
                 "libdl_android",
                 "libtimeinstate",
-                "libtflite",
                 "server_configurable_flags",
                 "libimage_io",
                 "libjpegdecoder",
diff --git a/core/proto/android/nfc/nfc_service.proto b/core/proto/android/nfc/nfc_service.proto
index 2df1d5d..1dcd5cc 100644
--- a/core/proto/android/nfc/nfc_service.proto
+++ b/core/proto/android/nfc/nfc_service.proto
@@ -60,7 +60,7 @@
     optional bool secure_nfc_capable = 13;
     optional bool vr_mode_enabled = 14;
     optional DiscoveryParamsProto discovery_params = 15;
-    optional P2pLinkManagerProto p2p_link_manager = 16;
+    reserved 16;
     optional com.android.nfc.cardemulation.CardEmulationManagerProto card_emulation_manager = 17;
     optional NfcDispatcherProto nfc_dispatcher = 18;
     optional string native_crash_logs = 19 [(.android.privacy).dest = DEST_EXPLICIT];
@@ -77,38 +77,6 @@
     optional bool enable_p2p = 5;
 }
 
-// Debugging information for com.android.nfc.P2pLinkManager
-message P2pLinkManagerProto {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
-    enum LinkState {
-        LINK_STATE_UNKNOWN = 0;
-        LINK_STATE_DOWN = 1;
-        LINK_STATE_DEBOUNCE = 2;
-        LINK_STATE_UP = 3;
-    }
-
-    enum SendState {
-        SEND_STATE_UNKNOWN = 0;
-        SEND_STATE_NOTHING_TO_SEND = 1;
-        SEND_STATE_NEED_CONFIRMATION = 2;
-        SEND_STATE_SENDING = 3;
-        SEND_STATE_COMPLETE = 4;
-        SEND_STATE_CANCELED = 5;
-    }
-
-    optional int32 default_miu = 1;
-    optional int32 default_rw_size = 2;
-    optional LinkState link_state = 3;
-    optional SendState send_state = 4;
-    optional int32 send_flags = 5;
-    optional bool send_enabled = 6;
-    optional bool receive_enabled = 7;
-    optional string callback_ndef = 8 [(.android.privacy).dest = DEST_EXPLICIT];
-    optional .android.nfc.NdefMessageProto message_to_send = 9;
-    repeated string uris_to_send = 10 [(.android.privacy).dest = DEST_EXPLICIT];
-}
-
 // Debugging information for com.android.nfc.NfcDispatcher
 message NfcDispatcherProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f937390..52d6f55 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -176,6 +176,7 @@
     <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
     <protected-broadcast android:name="android.bluetooth.device.action.SDP_RECORD" />
     <protected-broadcast android:name="android.bluetooth.device.action.BATTERY_LEVEL_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.REMOTE_ISSUE_OCCURRED" />
     <protected-broadcast android:name="android.bluetooth.devicepicker.action.LAUNCH" />
     <protected-broadcast android:name="android.bluetooth.devicepicker.action.DEVICE_SELECTED" />
     <protected-broadcast
@@ -2823,6 +2824,13 @@
     <permission android:name="android.permission.BIND_INCALL_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by a {@link android.telecom.CallStreamingService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+         @SystemApi @hide-->
+    <permission android:name="android.permission.BIND_CALL_STREAMING_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- Allows to query ongoing call details and manage ongoing calls
      <p>Protection level: signature|appop -->
     <permission android:name="android.permission.MANAGE_ONGOING_CALLS"
@@ -5537,6 +5545,12 @@
     <permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
                 android:protectionLevel="signature|installer" />
 
+    <!-- @SystemApi Allows an application to manage the holders of roles associated with default
+         applications.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_DEFAULT_APPLICATIONS"
+                android:protectionLevel="signature|role" />
+
     <!-- @SystemApi Allows an application to bypass role qualification. This allows switching role
          holders to otherwise non eligible holders. Only the shell is allowed to do this, the
          qualification for the shell role itself cannot be bypassed, and each role needs to
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 11a4ed7..d2ee5de 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1648,14 +1648,25 @@
             or has been granted the access to one of the attached USB devices/accessories.
         -->
         <flag name="connectedDevice" value="0x10" />
-        <!-- Managing a media projection session, e.g, for screen recording or taking
-            screenshots.
+        <!-- Managing a {@link android.media.projection.MediaProjection MediaProjection} session,
+             e.g., for screen recording or takingscreenshots.
 
-            <p>For apps with <code>targetSdkVersion</code>
-            {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a foreground
-            service with this type will require permission
-            {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROJECTION}, and the user
-            must have allowed the screen capture request from this app.
+             <p>
+             To capture through {@link android.media.projection.MediaProjection}, an app must start
+             a foreground service with the type corresponding to this constant. This type should
+             only be used for {@link android.media.projection.MediaProjection}. Capturing screen
+             contents via
+             {@link android.media.projection.MediaProjection#createVirtualDisplay(String, int, int,
+             int, int, android.view.Surface, android.hardware.display.VirtualDisplay.Callback,
+             android.os.Handler) createVirtualDisplay} conveniently allows recording, presenting
+             screen contents into a meeting, taking screenshots, or several other scenarios.
+             </p>
+
+             <p>For apps with <code>targetSdkVersion</code>
+             {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} and above, starting a
+             foreground service with this type will require permission
+             {@link android.Manifest.permission#FOREGROUND_SERVICE_MEDIA_PROJECTION}, and the user
+             must have allowed the screen capture request from this app.
         -->
         <flag name="mediaProjection" value="0x20" />
         <!-- Use the camera device or record video.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c75ee93..8e5ae9c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2997,6 +2997,12 @@
     <string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent"
             >com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
 
+    <!-- Package name of the system service that implements the shared connectivity service -->
+    <string translatable="false" name="shared_connectivity_service_package"></string>
+
+    <!-- Intent action used when binding to the shared connectivity service -->
+    <string translatable="false" name="shared_connectivity_service_intent_action"></string>
+
     <!-- Component name of the activity that shows the usb containment status. -->
     <string name="config_usbContaminantActivity" translatable="false"
             >com.android.systemui/com.android.systemui.usb.UsbContaminantActivity</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d7cdf73..91d5692 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4938,6 +4938,9 @@
   <java-symbol type="bool" name="config_stopSystemPackagesByDefault"/>
   <java-symbol type="string" name="config_wearServiceComponent" />
 
+  <java-symbol type="string" name="shared_connectivity_service_package" />
+  <java-symbol type="string" name="shared_connectivity_service_intent_action" />
+
   <!-- Whether to show weather on the lockscreen by default. -->
   <java-symbol type="bool" name="config_lockscreenWeatherEnabledByDefault" />
 </resources>
diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
index 2a1881e..f97099d 100644
--- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
+++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java
@@ -20,28 +20,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.os.BackgroundThread;
-
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-import java.time.Duration;
 
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -50,23 +35,11 @@
     private static final String SENSOR_NAME = "VirtualSensorName";
     private static final String SENSOR_VENDOR = "VirtualSensorVendor";
 
-    @Rule
-    public final MockitoRule mockito = MockitoJUnit.rule();
-
-    @Mock
-    private VirtualSensor.SensorStateChangeCallback mSensorCallback;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
-
     @Test
     public void parcelAndUnparcel_matches() {
         final VirtualSensorConfig originalConfig =
                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
                         .setVendor(SENSOR_VENDOR)
-                        .setStateChangeCallback(BackgroundThread.getExecutor(), mSensorCallback)
                         .build();
         final Parcel parcel = Parcel.obtain();
         originalConfig.writeToParcel(parcel, /* flags= */ 0);
@@ -76,7 +49,6 @@
         assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType());
         assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName());
         assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor());
-        assertThat(recreatedConfig.getStateChangeCallback()).isNotNull();
     }
 
     @Test
@@ -84,23 +56,5 @@
         final VirtualSensorConfig config =
                 new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build();
         assertThat(config.getVendor()).isNull();
-        assertThat(config.getStateChangeCallback()).isNull();
-    }
-
-    @Test
-    public void sensorConfig_sensorCallbackInvocation() throws Exception {
-        final VirtualSensorConfig config =
-                new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
-                        .setStateChangeCallback(BackgroundThread.getExecutor(), mSensorCallback)
-                        .build();
-
-        final Duration samplingPeriod = Duration.ofMillis(123);
-        final Duration batchLatency = Duration.ofMillis(456);
-
-        config.getStateChangeCallback().onStateChanged(true,
-                (int) MILLISECONDS.toMicros(samplingPeriod.toMillis()),
-                (int) MILLISECONDS.toMicros(batchLatency.toMillis()));
-
-        verify(mSensorCallback, timeout(1000)).onStateChanged(true, samplingPeriod, batchLatency);
     }
 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 248420f..0d7c8b8 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -46,7 +46,7 @@
     // The number of fields tested in the corresponding CTS AccessibilityNodeInfoTest:
     // See fullyPopulateAccessibilityNodeInfo, assertEqualsAccessibilityNodeInfo,
     // and assertAccessibilityNodeInfoCleared in that class.
-    private static final int NUM_MARSHALLED_PROPERTIES = 42;
+    private static final int NUM_MARSHALLED_PROPERTIES = 43;
 
     /**
      * The number of properties that are purposely not marshalled
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
index b085b73..34bf9e0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java
@@ -76,6 +76,7 @@
                             + " mDisplayAreasInfo.get():" + mDisplayAreasInfo.get(displayId));
         }
 
+        leash.setUnreleasedWarningCallSite("RootDisplayAreaOrganizer.onDisplayAreaAppeared");
         mDisplayAreasInfo.put(displayId, displayAreaInfo);
         mLeashes.put(displayId, leash);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index ca977ed..544d757 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -122,6 +122,8 @@
                             + " mDisplayAreasInfo.get():" + mDisplayAreasInfo.get(displayId));
         }
 
+        leash.setUnreleasedWarningCallSite(
+                "RootTaskDisplayAreaOrganizer.onDisplayAreaAppeared");
         mDisplayAreasInfo.put(displayId, displayAreaInfo);
         mLeashes.put(displayId, leash);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index b5ef72a..585f81c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -464,6 +464,9 @@
 
     @Override
     public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+        if (leash != null) {
+            leash.setUnreleasedWarningCallSite("ShellTaskOrganizer.onTaskAppeared");
+        }
         synchronized (mLock) {
             onTaskAppeared(new TaskAppearedInfo(taskInfo, leash));
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 5e46023..5e42782 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -223,7 +223,7 @@
             }
             final Display display = mDisplayController.getDisplay(mDisplayId);
             SurfaceControlViewHost viewRoot =
-                    new SurfaceControlViewHost(view.getContext(), display, wwm);
+                    new SurfaceControlViewHost(view.getContext(), display, wwm, "SystemWindows");
             attrs.flags |= FLAG_HARDWARE_ACCELERATED;
             viewRoot.setView(view, attrs);
             mViewRoots.put(view, viewRoot);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index fa3a6ad..e44257e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -114,7 +114,8 @@
         context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY,
                 null /* options */);
         mHostLeash = rootLeash;
-        mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this);
+        mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this,
+                "SplitDecorManager");
 
         mIconSize = context.getResources().getDimensionPixelSize(R.dimen.split_icon_size);
         final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
index 6b5ddcb..eb3c1df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java
@@ -113,7 +113,8 @@
                     "Try to inflate divider view again without release first");
         }
 
-        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this,
+                "SplitWindowManager");
         mDividerView = (DividerView) LayoutInflater.from(mContext)
                 .inflate(R.layout.split_divider, null /* root */);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
index 34e650a..efd4594 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -363,7 +363,8 @@
     /** Creates a {@link SurfaceControlViewHost} for this window manager. */
     @VisibleForTesting(visibility = PRIVATE)
     public SurfaceControlViewHost createSurfaceViewHost() {
-        return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this,
+                getClass().getSimpleName());
     }
 
     /** Gets the layout params. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
index f376e1f..32894cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java
@@ -117,6 +117,7 @@
     @Override
     public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
             @NonNull SurfaceControl leash) {
+        leash.setUnreleasedWarningCallSite("HideDisplayCutoutOrganizer.onDisplayAreaAppeared");
         if (!addDisplayAreaInfoAndLeashToMap(displayAreaInfo, leash)) {
             return;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
index 8ebcd81..71cc8df 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/BackgroundWindowManager.java
@@ -122,7 +122,8 @@
             return false;
         }
 
-        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+        mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this,
+                "BackgroundWindowManager");
         mBackgroundView = (View) LayoutInflater.from(mContext)
                 .inflate(R.layout.background_panel, null /* root */);
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index 451afa0..38ce164 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -154,6 +154,8 @@
     @Override
     public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
             @NonNull SurfaceControl leash) {
+        leash.setUnreleasedWarningCallSite(
+                "OneHandedSiaplyAreaOrganizer.onDisplayAreaAppeared");
         mDisplayAreaTokenMap.put(displayAreaInfo.token, leash);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index ec34f73..00ed094 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1215,6 +1215,9 @@
         @Override
         public void stopSwipePipToHome(int taskId, ComponentName componentName,
                 Rect destinationBounds, SurfaceControl overlay) {
+            if (overlay != null) {
+                overlay.setUnreleasedWarningCallSite("PipController.stopSwipePipToHome");
+            }
             executeRemoteCallWithTaskPermission(mController, "stopSwipePipToHome",
                     (controller) -> {
                         controller.stopSwipePipToHome(taskId, componentName, destinationBounds,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index ae685ad..91b0aa1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -484,7 +484,7 @@
 
     interface SurfaceControlViewHostFactory {
         default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) {
-            return new SurfaceControlViewHost(c, d, wmm);
+            return new SurfaceControlViewHost(c, d, wmm, "WindowDecoration");
         }
     }
 
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 10c287d..23a7520 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -1,5 +1,6 @@
 #undef LOG_TAG
 #define LOG_TAG "Bitmap"
+// #define LOG_NDEBUG 0
 #include "Bitmap.h"
 
 #include <hwui/Bitmap.h>
@@ -372,15 +373,33 @@
     return srcPM.readPixels(dstPM);
 }
 
-static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
-                           jint dstConfigHandle, jboolean isMutable) {
+static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle,
+                           jboolean isMutable) {
+    LocalScopedBitmap bitmapHolder(srcHandle);
+    if (!bitmapHolder.valid()) {
+        return NULL;
+    }
+    const Bitmap& original = bitmapHolder->bitmap();
+    const bool hasGainmap = original.hasGainmap();
     SkBitmap src;
-    reinterpret_cast<BitmapWrapper*>(srcHandle)->getSkBitmap(&src);
+    bitmapHolder->getSkBitmap(&src);
+
     if (dstConfigHandle == GraphicsJNI::hardwareLegacyBitmapConfig()) {
         sk_sp<Bitmap> bitmap(Bitmap::allocateHardwareBitmap(src));
         if (!bitmap.get()) {
             return NULL;
         }
+        if (hasGainmap) {
+            auto gainmap = sp<uirenderer::Gainmap>::make();
+            gainmap->info = original.gainmap()->info;
+            const SkBitmap skSrcBitmap = original.gainmap()->bitmap->getSkBitmap();
+            sk_sp<Bitmap> skBitmap(Bitmap::allocateHardwareBitmap(skSrcBitmap));
+            if (!skBitmap.get()) {
+                return NULL;
+            }
+            gainmap->bitmap = std::move(skBitmap);
+            bitmap->setGainmap(std::move(gainmap));
+        }
         return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(isMutable));
     }
 
@@ -392,6 +411,18 @@
         return NULL;
     }
     auto bitmap = allocator.getStorageObjAndReset();
+    if (hasGainmap) {
+        auto gainmap = sp<uirenderer::Gainmap>::make();
+        gainmap->info = original.gainmap()->info;
+        const SkBitmap skSrcBitmap = original.gainmap()->bitmap->getSkBitmap();
+        SkBitmap skDestBitmap;
+        HeapAllocator destAllocator;
+        if (!bitmapCopyTo(&skDestBitmap, dstCT, skSrcBitmap, &destAllocator)) {
+            return NULL;
+        }
+        gainmap->bitmap = sk_sp<Bitmap>(destAllocator.getStorageObjAndReset());
+        bitmap->setGainmap(std::move(gainmap));
+    }
     return createBitmap(env, bitmap, getPremulBitmapCreateFlags(isMutable));
 }
 
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index a55de95..00919dc 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -33,7 +33,8 @@
 // Cache size limits.
 static const size_t maxKeySize = 1024;
 static const size_t maxValueSize = 2 * 1024 * 1024;
-static const size_t maxTotalSize = 1024 * 1024;
+static const size_t maxTotalSize = 4 * 1024 * 1024;
+static_assert(maxKeySize + maxValueSize < maxTotalSize);
 
 ShaderCache::ShaderCache() {
     // There is an "incomplete FileBlobCache type" compilation error, if ctor is moved to header.
@@ -175,14 +176,13 @@
 
 void ShaderCache::saveToDiskLocked() {
     ATRACE_NAME("ShaderCache::saveToDiskLocked");
-    if (mInitialized && mBlobCache && mSavePending) {
+    if (mInitialized && mBlobCache) {
         if (mIDHash.size()) {
             auto key = sIDKey;
             set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size());
         }
         mBlobCache->writeToFile();
     }
-    mSavePending = false;
 }
 
 void ShaderCache::store(const SkData& key, const SkData& data, const SkString& /*description*/) {
@@ -225,10 +225,10 @@
     }
     set(bc, key.data(), keySize, value, valueSize);
 
-    if (!mSavePending && mDeferredSaveDelay > 0) {
+    if (!mSavePending && mDeferredSaveDelayMs > 0) {
         mSavePending = true;
         std::thread deferredSaveThread([this]() {
-            sleep(mDeferredSaveDelay);
+            usleep(mDeferredSaveDelayMs * 1000);  // milliseconds to microseconds
             std::lock_guard<std::mutex> lock(mMutex);
             // Store file on disk if there a new shader or Vulkan pipeline cache size changed.
             if (mCacheDirty || mNewPipelineCacheSize != mOldPipelineCacheSize) {
@@ -237,6 +237,7 @@
                 mTryToStorePipelineCache = false;
                 mCacheDirty = false;
             }
+            mSavePending = false;
         });
         deferredSaveThread.detach();
     }
diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h
index bc35fa5f..f5506d6 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.h
+++ b/libs/hwui/pipeline/skia/ShaderCache.h
@@ -156,7 +156,8 @@
      * pending.  Each time a key/value pair is inserted into the cache via
      * load, a deferred save is initiated if one is not already pending.
      * This will wait some amount of time and then trigger a save of the cache
-     * contents to disk.
+     * contents to disk, unless mDeferredSaveDelayMs is 0 in which case saving
+     * is disabled.
      */
     bool mSavePending = false;
 
@@ -166,9 +167,11 @@
     size_t mObservedBlobValueSize = 20 * 1024;
 
     /**
-     *  The time in seconds to wait before saving newly inserted cache entries.
+     *  The time in milliseconds to wait before saving newly inserted cache entries.
+     *
+     *  WARNING: setting this to 0 will disable writing the cache to disk.
      */
-    unsigned int mDeferredSaveDelay = 4;
+    unsigned int mDeferredSaveDelayMs = 4 * 1000;
 
     /**
      * "mMutex" is the mutex used to prevent concurrent access to the member
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 576e946..7bcd45c 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#include <GrDirectContext.h>
+#include <Properties.h>
+#include <SkData.h>
+#include <SkRefCnt.h>
 #include <cutils/properties.h>
 #include <dirent.h>
 #include <errno.h>
@@ -22,11 +26,12 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <utils/Log.h>
+
 #include <cstdint>
+
 #include "FileBlobCache.h"
 #include "pipeline/skia/ShaderCache.h"
-#include <SkData.h>
-#include <SkRefCnt.h>
+#include "tests/common/TestUtils.h"
 
 using namespace android::uirenderer::skiapipeline;
 
@@ -37,11 +42,38 @@
 class ShaderCacheTestUtils {
 public:
     /**
-     * "setSaveDelay" sets the time in seconds to wait before saving newly inserted cache entries.
-     * If set to 0, then deferred save is disabled.
+     * Hack to reset all member variables of the given cache to their default / initial values.
+     *
+     * WARNING: this must be kept up to date manually, since ShaderCache's parent disables just
+     * reassigning a new instance.
      */
-    static void setSaveDelay(ShaderCache& cache, unsigned int saveDelay) {
-        cache.mDeferredSaveDelay = saveDelay;
+    static void reinitializeAllFields(ShaderCache& cache) {
+        ShaderCache newCache = ShaderCache();
+        std::lock_guard<std::mutex> lock(cache.mMutex);
+        // By order of declaration
+        cache.mInitialized = newCache.mInitialized;
+        cache.mBlobCache.reset(nullptr);
+        cache.mFilename = newCache.mFilename;
+        cache.mIDHash.clear();
+        cache.mSavePending = newCache.mSavePending;
+        cache.mObservedBlobValueSize = newCache.mObservedBlobValueSize;
+        cache.mDeferredSaveDelayMs = newCache.mDeferredSaveDelayMs;
+        cache.mTryToStorePipelineCache = newCache.mTryToStorePipelineCache;
+        cache.mInStoreVkPipelineInProgress = newCache.mInStoreVkPipelineInProgress;
+        cache.mNewPipelineCacheSize = newCache.mNewPipelineCacheSize;
+        cache.mOldPipelineCacheSize = newCache.mOldPipelineCacheSize;
+        cache.mCacheDirty = newCache.mCacheDirty;
+        cache.mNumShadersCachedInRam = newCache.mNumShadersCachedInRam;
+    }
+
+    /**
+     * "setSaveDelayMs" sets the time in milliseconds to wait before saving newly inserted cache
+     * entries. If set to 0, then deferred save is disabled, and "saveToDiskLocked" must be called
+     * manually, as seen in the "terminate" testing helper function.
+     */
+    static void setSaveDelayMs(ShaderCache& cache, unsigned int saveDelayMs) {
+        std::lock_guard<std::mutex> lock(cache.mMutex);
+        cache.mDeferredSaveDelayMs = saveDelayMs;
     }
 
     /**
@@ -50,8 +82,9 @@
      */
     static void terminate(ShaderCache& cache, bool saveContent) {
         std::lock_guard<std::mutex> lock(cache.mMutex);
-        cache.mSavePending = saveContent;
-        cache.saveToDiskLocked();
+        if (saveContent) {
+            cache.saveToDiskLocked();
+        }
         cache.mBlobCache = NULL;
     }
 
@@ -62,6 +95,38 @@
     static bool validateCache(ShaderCache& cache, std::vector<T> hash) {
         return cache.validateCache(hash.data(), hash.size() * sizeof(T));
     }
+
+    /**
+     * Waits until cache::mSavePending is false, checking every 0.1 ms *while the mutex is free*.
+     *
+     * Fails if there was no save pending, or if the cache was already being written to disk, or if
+     * timeoutMs is exceeded.
+     *
+     * Note: timeoutMs only guards against mSavePending getting stuck like in b/268205519, and
+     * cannot protect against mutex-based deadlock. Reaching timeoutMs implies something is broken,
+     * so setting it to a sufficiently large value will not delay execution in the happy state.
+     */
+    static void waitForPendingSave(ShaderCache& cache, const int timeoutMs = 50) {
+        {
+            std::lock_guard<std::mutex> lock(cache.mMutex);
+            ASSERT_TRUE(cache.mSavePending);
+        }
+        bool saving = true;
+        float elapsedMilliseconds = 0;
+        while (saving) {
+            if (elapsedMilliseconds >= timeoutMs) {
+                FAIL() << "Timed out after waiting " << timeoutMs << " ms for a pending save";
+            }
+            // This small (0.1 ms) delay is to avoid working too much while waiting for
+            // deferredSaveThread to take the mutex and start the disk write.
+            const int delayMicroseconds = 100;
+            usleep(delayMicroseconds);
+            elapsedMilliseconds += (float)delayMicroseconds / 1000;
+
+            std::lock_guard<std::mutex> lock(cache.mMutex);
+            saving = cache.mSavePending;
+        }
+    }
 };
 
 } /* namespace skiapipeline */
@@ -83,6 +148,18 @@
     return false;
 }
 
+/**
+ * Attempts to delete the given file, and asserts that either:
+ * 1. Deletion was successful, OR
+ * 2. The file did not exist.
+ *
+ * Tip: wrap calls to this in ASSERT_NO_FATAL_FAILURE(x) if a test should exit early if this fails.
+ */
+void deleteFileAssertSuccess(const std::string& filePath) {
+    int deleteResult = remove(filePath.c_str());
+    ASSERT_TRUE(0 == deleteResult || ENOENT == errno);
+}
+
 inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
     return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
            0 == memcmp(shader1->data(), shader2->data(), shader1->size());
@@ -93,6 +170,10 @@
     return checkShader(shader, shader2);
 }
 
+inline bool checkShader(const sk_sp<SkData>& shader, const std::string& program) {
+    return checkShader(shader, program.c_str());
+}
+
 template <typename T>
 bool checkShader(const sk_sp<SkData>& shader, std::vector<T>& program) {
     sk_sp<SkData> shader2 = SkData::MakeWithCopy(program.data(), program.size() * sizeof(T));
@@ -103,6 +184,10 @@
     shader = SkData::MakeWithCString(program);
 }
 
+void setShader(sk_sp<SkData>& shader, const std::string& program) {
+    setShader(shader, program.c_str());
+}
+
 template <typename T>
 void setShader(sk_sp<SkData>& shader, std::vector<T>& buffer) {
     shader = SkData::MakeWithCopy(buffer.data(), buffer.size() * sizeof(T));
@@ -126,13 +211,13 @@
     std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
     // remove any test files from previous test run
-    int deleteFile = remove(cacheFile1.c_str());
-    ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
     std::srand(0);
 
     // read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
+    ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 0);  // disable deferred save
     ShaderCache::get().initShaderDiskCache();
 
     // read a key - should not be found since the cache is empty
@@ -186,7 +271,8 @@
     ASSERT_TRUE(checkShader(outVS2, dataBuffer));
 
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
-    remove(cacheFile1.c_str());
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
 }
 
 TEST(ShaderCacheTest, testCacheValidation) {
@@ -198,13 +284,13 @@
     std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
 
     // remove any test files from previous test run
-    int deleteFile = remove(cacheFile1.c_str());
-    ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
     std::srand(0);
 
     // generate identity and read the cache from a file that does not exist
     ShaderCache::get().setFilename(cacheFile1.c_str());
-    ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0);  // disable deferred save
+    ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 0);  // disable deferred save
     std::vector<uint8_t> identity(1024);
     genRandomData(identity);
     ShaderCache::get().initShaderDiskCache(
@@ -278,7 +364,81 @@
     }
 
     ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
-    remove(cacheFile1.c_str());
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile1));
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile2));
+}
+
+using namespace android::uirenderer;
+RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+    if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
+        // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+        GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
+    }
+    if (!folderExist(getExternalStorageFolder())) {
+        // Don't run the test if external storage folder is not available
+        return;
+    }
+    std::string cacheFile = getExternalStorageFolder() + "/shaderCacheTest";
+    GrDirectContext* grContext = renderThread.getGrContext();
+
+    // Remove any test files from previous test run
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile));
+
+    // The first iteration of this loop is to save an initial VkPipelineCache data blob to disk,
+    // which sets up the second iteration for a common scenario of comparing a "new" VkPipelineCache
+    // blob passed to "store" against the same blob that's already in the persistent cache from a
+    // previous launch. "reinitializeAllFields" is critical to emulate each iteration being as close
+    // to the state of a freshly launched app as possible, as the initial values of member variables
+    // like mInStoreVkPipelineInProgress and mOldPipelineCacheSize are critical to catch issues
+    // such as b/268205519
+    for (int flushIteration = 1; flushIteration <= 2; flushIteration++) {
+        SCOPED_TRACE("Frame flush iteration " + std::to_string(flushIteration));
+        // Reset *all* in-memory data and reload the cache from disk.
+        ShaderCacheTestUtils::reinitializeAllFields(ShaderCache::get());
+        ShaderCacheTestUtils::setSaveDelayMs(ShaderCache::get(), 10);  // Delay must be > 0 to save.
+        ShaderCache::get().setFilename(cacheFile.c_str());
+        ShaderCache::get().initShaderDiskCache();
+
+        // 1st iteration: store pipeline data to be read back on a subsequent "boot" of the "app".
+        // 2nd iteration: ensure that an initial frame flush (without storing any shaders) given the
+        // same pipeline data that's already on disk doesn't break the cache.
+        ShaderCache::get().onVkFrameFlushed(grContext);
+        ASSERT_NO_FATAL_FAILURE(ShaderCacheTestUtils::waitForPendingSave(ShaderCache::get()));
+    }
+
+    constexpr char shader1[] = "sassas";
+    constexpr char shader2[] = "someVS";
+    constexpr int numIterations = 3;
+    // Also do n iterations of separate "store some shaders then flush the frame" pairs to just
+    // double-check the cache also doesn't get stuck from that use case.
+    for (int saveIteration = 1; saveIteration <= numIterations; saveIteration++) {
+        SCOPED_TRACE("Shader save iteration " + std::to_string(saveIteration));
+        // Write twice to the in-memory cache, which should start a deferred save with both queued.
+        sk_sp<SkData> inVS;
+        setShader(inVS, shader1 + std::to_string(saveIteration));
+        ShaderCache::get().store(GrProgramDescTest(100), *inVS.get(), SkString());
+        setShader(inVS, shader2 + std::to_string(saveIteration));
+        ShaderCache::get().store(GrProgramDescTest(432), *inVS.get(), SkString());
+
+        // Simulate flush to also save latest pipeline info.
+        ShaderCache::get().onVkFrameFlushed(grContext);
+        ASSERT_NO_FATAL_FAILURE(ShaderCacheTestUtils::waitForPendingSave(ShaderCache::get()));
+    }
+
+    // Reload from disk to ensure saving succeeded.
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+    ShaderCache::get().initShaderDiskCache();
+
+    // Read twice, ensure equal to last store.
+    sk_sp<SkData> outVS;
+    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(100))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS, shader1 + std::to_string(numIterations)));
+    ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
+    ASSERT_TRUE(checkShader(outVS, shader2 + std::to_string(numIterations)));
+
+    // Clean up.
+    ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
+    ASSERT_NO_FATAL_FAILURE(deleteFileAssertSuccess(cacheFile));
 }
 
 }  // namespace
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 6d65c26..f327e4e 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -29,6 +29,7 @@
 import android.os.ServiceManager;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.view.Surface;
 
 import java.util.Map;
 
@@ -74,8 +75,9 @@
      * Returns an {@link Intent} that <b>must</b> be passed to
      * {@link Activity#startActivityForResult(Intent, int)} (or similar) in order to start screen
      * capture. The activity will prompt the user whether to allow screen capture.  The result of
-     * this activity (received by overriding {@link Activity#onActivityResult(int, int, Intent)}
-     * should be passed to {@link #getMediaProjection(int, Intent)}.
+     * this activity (received by overriding {@link Activity#onActivityResult(int, int, Intent)
+     * onActivityResult(int, int, Intent)}) should be passed to
+     * {@link #getMediaProjection(int, Intent)}.
      * <p>
      * Identical to calling {@link #createScreenCaptureIntent(MediaProjectionConfig)} with
      * a {@link MediaProjectionConfig#createConfigForUserChoice()}.
@@ -102,8 +104,8 @@
      * capture. Customizes the activity and resulting {@link MediaProjection} session based up
      * the provided {@code config}. The activity will prompt the user whether to allow screen
      * capture. The result of this activity (received by overriding
-     * {@link Activity#onActivityResult(int, int, Intent)}) should be passed to
-     * {@link #getMediaProjection(int, Intent)}.
+     * {@link Activity#onActivityResult(int, int, Intent) onActivityResult(int, int, Intent)})
+     * should be passed to {@link #getMediaProjection(int, Intent)}.
      *
      * <p>
      * If {@link MediaProjectionConfig} was created from:
@@ -146,47 +148,48 @@
 
     /**
      * Retrieves the {@link MediaProjection} obtained from a successful screen
-     * capture request. The result code and data from the request are provided
-     * by overriding {@link Activity#onActivityResult(int, int, Intent)
-     * onActivityResult(int, int, Intent)}, which is called after starting an
-     * activity using {@link #createScreenCaptureIntent()}.
-     *
-     * <p>Starting from Android {@link android.os.Build.VERSION_CODES#R}, if
-     * your application requests the
-     * {@link android.Manifest.permission#SYSTEM_ALERT_WINDOW
-     * SYSTEM_ALERT_WINDOW} permission, and the user has not explicitly denied
-     * it, the permission will be automatically granted until the projection is
-     * stopped. The permission allows your app to display user controls on top
-     * of the screen being captured.
-     *
-     * <p>Apps targeting SDK version {@link android.os.Build.VERSION_CODES#Q} or
-     * later must set the
-     * {@link android.R.attr#foregroundServiceType foregroundServiceType}
-     * attribute to {@code mediaProjection} in the
-     * <a href="/guide/topics/manifest/service-element">
-     * <code>&lt;service&gt;</code></a> element of the app's manifest file;
-     * {@code mediaProjection} is equivalent to
+     * capture request. The result code and data from the request are provided by overriding
+     * {@link Activity#onActivityResult(int, int, Intent) onActivityResult(int, int, Intent)},
+     * which is called after starting an activity using {@link #createScreenCaptureIntent()}.
+     * <p>
+     * Starting from Android {@link android.os.Build.VERSION_CODES#R R}, if your application
+     * requests the {@link android.Manifest.permission#SYSTEM_ALERT_WINDOW SYSTEM_ALERT_WINDOW}
+     * permission, and the user has not explicitly denied it, the permission will be automatically
+     * granted until the projection is stopped. The permission allows your app to display user
+     * controls on top of the screen being captured.
+     * </p>
+     * <p>
+     * An app targeting SDK version {@link android.os.Build.VERSION_CODES#Q Q} or later must
+     * invoke {@code getMediaProjection} and maintain the capture session
+     * ({@link MediaProjection#createVirtualDisplay(String, int, int, int, int, Surface,
+     * android.hardware.display.VirtualDisplay.Callback, Handler)
+     * MediaProjection#createVirtualDisplay}) while running a foreground service. The app must set
+     * the {@link android.R.attr#foregroundServiceType foregroundServiceType} attribute to
      * {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
-     * FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}.
+     * FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION} in the
+     * <a href="/guide/topics/manifest/service-element"><code>&lt;service&gt;</code></a> element of
+     * the app's manifest file.
+     * </p>
      *
-     * @see <a href="/guide/components/foreground-services">
-     *      Foreground services developer guide</a>
-     * @see <a href="/guide/topics/large-screens/media-projection">
-     *      Media projection developer guide</a>
-     *
-     * @param resultCode The result code from
-     *      {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)
-     *      onActivityResult(int, int, Intent)}.
-     * @param resultData The result data from
-     *      {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)
-     *      onActivityResult(int, int, Intent)}.
-     * @return The media projection obtained from a successful screen capture
-     *      request, or null if the result of the screen capture request is not
-     *      {@link Activity#RESULT_OK RESULT_OK}.
+     * @param resultCode The result code from {@link Activity#onActivityResult(int, int, Intent)
+     *                   onActivityResult(int, int, Intent)}.
+     * @param resultData The result data from {@link Activity#onActivityResult(int, int, Intent)
+     *                   onActivityResult(int, int, Intent)}.
+     * @return The media projection obtained from a successful screen capture request, or null if
+     * the result of the screen capture request is not {@link Activity#RESULT_OK RESULT_OK}.
      * @throws IllegalStateException On
-     *      pre-{@link android.os.Build.VERSION_CODES#Q Q} devices if a
-     *      previously obtained {@code MediaProjection} from the same
-     *      {@code resultData} has not yet been stopped.
+     *                               pre-{@link android.os.Build.VERSION_CODES#Q Q} devices if a
+     *                               previously obtained {@code MediaProjection} from the same
+     *                               {@code resultData} has not yet been stopped.
+     * @throws SecurityException     On {@link android.os.Build.VERSION_CODES#Q Q}+ devices if not
+     *                               invoked from a foreground service with type
+     *                {@link android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
+     *                               FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION}, unless caller is a
+     *                               privileged app.
+     * @see <a href="/guide/components/foreground-services">
+     * Foreground services developer guide</a>
+     * @see <a href="/guide/topics/large-screens/media-projection">
+     * Media projection developer guide</a>
      */
     public MediaProjection getMediaProjection(int resultCode, @NonNull Intent resultData) {
         if (resultCode != Activity.RESULT_OK || resultData == null) {
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index de1b164..8459538 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -317,25 +317,21 @@
     /**
      * Time shift mode: off.
      * <p>Time shift is disabled.
-     * @hide
      */
     public static final int TIME_SHIFT_MODE_OFF = 1;
     /**
      * Time shift mode: local.
      * <p>Time shift is handle locally, using on-device data. E.g. playing a local file.
-     * @hide
      */
     public static final int TIME_SHIFT_MODE_LOCAL = 2;
     /**
      * Time shift mode: network.
      * <p>Time shift is handle remotely via network. E.g. online streaming.
-     * @hide
      */
     public static final int TIME_SHIFT_MODE_NETWORK = 3;
     /**
      * Time shift mode: auto.
      * <p>Time shift mode is handled automatically.
-     * @hide
      */
     public static final int TIME_SHIFT_MODE_AUTO = 4;
 
@@ -774,7 +770,11 @@
          * Informs the app available speeds for time-shifting.
          * @param session A {@link TvInputManager.Session} associated with this callback.
          * @param speeds An ordered array of playback speeds, expressed as values relative to the
-         *               normal playback speed 1.0.
+         *               normal playback speed (1.0), at which the current content can be played as
+         *               a time-shifted broadcast. This is an empty array if the supported playback
+         *               speeds are unknown or the video/broadcast is not in time shift mode. If
+         *               currently in time shift mode, this array will normally include at least
+         *               the values 1.0 (normal speed) and 0.0 (paused).
          * @see PlaybackParams#getSpeed()
          */
         public void onAvailableSpeeds(Session session, float[] speeds) {
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 000ed3b..4bc137d 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1117,7 +1117,6 @@
          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
-         * @hide
          */
         public void notifyTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -1141,9 +1140,12 @@
          * <p>This should be called when time-shifting is enabled.
          *
          * @param speeds An ordered array of playback speeds, expressed as values relative to the
-         *               normal playback speed 1.0.
+         *               normal playback speed (1.0), at which the current content can be played as
+         *               a time-shifted broadcast. This is an empty array if the supported playback
+         *               speeds are unknown or the video/broadcast is not in time shift mode. If
+         *               currently in time shift mode, this array will normally include at least
+         *               the values 1.0 (normal speed) and 0.0 (paused).
          * @see PlaybackParams#getSpeed()
-         * @hide
          */
         public void notifyAvailableSpeeds(@NonNull float[] speeds) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -1191,7 +1193,6 @@
          *
          * @param available {@code true} if cueing message is available; {@code false} if it becomes
          *                  unavailable.
-         * @hide
          */
         public void notifyCueingMessageAvailability(boolean available) {
             executeOrPostRunnableOnMainThread(new Runnable() {
@@ -1575,7 +1576,6 @@
          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
-         * @hide
          */
         public void onTimeShiftSetMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
         }
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 372fa6d..3ef61f2 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -633,7 +633,6 @@
      * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
      * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
      * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
-     * @hide
      */
     public void timeShiftSetMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
         if (mSession != null) {
@@ -1193,7 +1192,6 @@
          * @param inputId The ID of the TV input bound to this view.
          * @param available The current availability of cueing message. {@code true} if cueing
          *                  message is available; {@code false} if it becomes unavailable.
-         * @hide
          */
         public void onCueingMessageAvailability(@NonNull String inputId, boolean available) {
         }
@@ -1206,7 +1204,6 @@
          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
-         * @hide
          */
         public void onTimeShiftMode(
                 @NonNull String inputId, @TvInputManager.TimeShiftMode int mode) {
@@ -1217,11 +1214,14 @@
          *
          * @param inputId The ID of the TV input bound to this view.
          * @param speeds An ordered array of playback speeds, expressed as values relative to the
-         *               normal playback speed 1.0.
+         *               normal playback speed (1.0), at which the current content can be played as
+         *               a time-shifted broadcast. This is an empty array if the supported playback
+         *               speeds are unknown or the video/broadcast is not in time shift mode. If
+         *               currently in time shift mode, this array will normally include at least
+         *               the values 1.0 (normal speed) and 0.0 (paused).
          * @see PlaybackParams#getSpeed()
-         * @hide
          */
-        public void onAvailableSpeeds(@NonNull String inputId, float[] speeds) {
+        public void onAvailableSpeeds(@NonNull String inputId, @NonNull float[] speeds) {
         }
 
         /**
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 80b6e21..4be5523 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -231,41 +231,34 @@
      * Time shift command type: play.
      *
      * @see TvView#timeShiftPlay(String, Uri)
-     * @hide
      */
     public static final String TIME_SHIFT_COMMAND_TYPE_PLAY = "play";
     /**
      * Time shift command type: pause.
      *
      * @see TvView#timeShiftPause()
-     * @hide
      */
     public static final String TIME_SHIFT_COMMAND_TYPE_PAUSE = "pause";
     /**
      * Time shift command type: resume.
      *
      * @see TvView#timeShiftResume()
-     * @hide
      */
     public static final String TIME_SHIFT_COMMAND_TYPE_RESUME = "resume";
     /**
      * Time shift command type: seek to.
      *
      * @see TvView#timeShiftSeekTo(long)
-     * @hide
      */
     public static final String TIME_SHIFT_COMMAND_TYPE_SEEK_TO = "seek_to";
     /**
      * Time shift command type: set playback params.
      *
      * @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
-     * @hide
      */
     public static final String TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS = "set_playback_params";
     /**
      * Time shift command type: set time shift mode.
-     *
-     * @hide
      */
     public static final String TIME_SHIFT_COMMAND_TYPE_SET_MODE = "set_mode";
 
@@ -274,7 +267,6 @@
      * <p>Type: android.net.Uri
      *
      * @see #TIME_SHIFT_COMMAND_TYPE_PLAY
-     * @hide
      */
     public static final String COMMAND_PARAMETER_KEY_PROGRAM_URI = "command_program_uri";
     /**
@@ -282,7 +274,6 @@
      * <p>Type: long
      *
      * @see #TIME_SHIFT_COMMAND_TYPE_SEEK_TO
-     * @hide
      */
     public static final String COMMAND_PARAMETER_KEY_TIME_POSITION = "command_time_position";
     /**
@@ -290,7 +281,6 @@
      * <p>Type: android.media.PlaybackParams
      *
      * @see #TIME_SHIFT_COMMAND_TYPE_SET_PLAYBACK_PARAMS
-     * @hide
      */
     public static final String COMMAND_PARAMETER_KEY_PLAYBACK_PARAMS = "command_playback_params";
     /**
@@ -301,7 +291,6 @@
      * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
      *
      * @see #TIME_SHIFT_COMMAND_TYPE_SET_MODE
-     * @hide
      */
     public static final String COMMAND_PARAMETER_KEY_TIME_SHIFT_MODE = "command_time_shift_mode";
 
@@ -589,18 +578,24 @@
 
         /**
          * Receives current time shift mode.
+         *
          * @param mode The current time shift mode. The value is one of the following:
          * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
          * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
          * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
-         * @hide
          */
         public void onTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
         }
 
         /**
-         * Receives available speeds.
-         * @hide
+         * Receives available playback speeds.
+         *
+         * @param speeds An ordered array of playback speeds, expressed as values relative to the
+         *               normal playback speed (1.0), at which the current content can be played as
+         *               a time-shifted broadcast. This is an empty array if the supported playback
+         *               speeds are unknown or the video/broadcast is not in time shift mode. If
+         *               currently in time shift mode, this array will normally include at least
+         *               the values 1.0 (normal speed) and 0.0 (paused).
          */
         public void onAvailableSpeeds(@NonNull float[] speeds) {
         }
@@ -742,8 +737,8 @@
         /**
          * Called when the time shift {@link android.media.PlaybackParams} is set or changed.
          *
+         * @param params The new {@link PlaybackParams} that was set or changed.
          * @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
-         * @hide
          */
         public void onTimeShiftPlaybackParams(@NonNull PlaybackParams params) {
         }
@@ -753,17 +748,25 @@
          *
          * @see TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)
          * @see android.media.tv.TvInputService.Session#notifyTimeShiftStatusChanged(int)
-         * @hide
+         * @param inputId The ID of the input for which the time shift status has changed.
+         * @param status The status of which the input has changed to. Should be one of the
+         *               following.
+         *               <ul>
+         *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}
+         *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
+         *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
+         *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
+         *               </ul>
          */
         public void onTimeShiftStatusChanged(
-                @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {
-        }
+                @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {}
 
         /**
          * Called when time shift start position is changed.
          *
          * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged(String, long)
-         * @hide
+         * @param inputId The ID of the input for which the time shift start position has changed.
+         * @param timeMs The start position for time shifting, in milliseconds since the epoch.
          */
         public void onTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) {
         }
@@ -772,7 +775,8 @@
          * Called when time shift current position is changed.
          *
          * @see TvView.TimeShiftPositionCallback#onTimeShiftCurrentPositionChanged(String, long)
-         * @hide
+         * @param inputId The ID of the input for which the time shift current position has changed.
+         * @param timeMs The current position for time shifting, in milliseconds since the epoch.
          */
         public void onTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) {
         }
@@ -1086,7 +1090,6 @@
          *
          * @param cmdType type of the specific command
          * @param parameters parameters of the specific command
-         * @hide
          */
         @CallSuper
         public void sendTimeShiftCommandRequest(
@@ -1275,7 +1278,6 @@
 
         /**
          * Requests time shift mode.
-         * @hide
          */
         @CallSuper
         public void requestTimeShiftMode() {
@@ -1299,7 +1301,6 @@
 
         /**
          * Requests available speeds for time shift.
-         * @hide
          */
         @CallSuper
         public void requestAvailableSpeeds() {
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
index 2c40f39..ca47c2c 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java
@@ -598,13 +598,12 @@
     }
 
     /**
-     * Sends current time shift mode to related TV interactive app.
+     * Sends the current time shift mode to the TV interactive app bound to this view
      *
      * @param mode The current time shift mode. The value is one of the following:
      * {@link TvInputManager#TIME_SHIFT_MODE_OFF}, {@link TvInputManager#TIME_SHIFT_MODE_LOCAL},
      * {@link TvInputManager#TIME_SHIFT_MODE_NETWORK},
      * {@link TvInputManager#TIME_SHIFT_MODE_AUTO}.
-     * @hide
      */
     public void sendTimeShiftMode(@android.media.tv.TvInputManager.TimeShiftMode int mode) {
         if (DEBUG) {
@@ -616,12 +615,15 @@
     }
 
     /**
-     * Sends available supported speeds to related TV interactive app.
+     * Sends the available supported playback speeds to the TV interactive app bound to this view.
      *
-     * @param speeds An ordered array of playback speeds, expressed as values relative to the normal
-     *               playback speed 1.0.
+     * @param speeds An ordered array of playback speeds, expressed as values relative to the
+     *               normal playback speed (1.0), at which the current content can be played as
+     *               a time-shifted broadcast. This is an empty array if the supported playback
+     *               speeds are unknown or the video/broadcast is not in time shift mode. If
+     *               currently in time shift mode, this array will normally include at least
+     *               the values 1.0 (normal speed) and 0.0 (paused).
      * @see PlaybackParams#getSpeed()
-     * @hide
      */
     public void sendAvailableSpeeds(@NonNull float[] speeds) {
         if (DEBUG) {
@@ -743,7 +745,7 @@
      * {@link android.media.PlaybackParams} is set or changed.
      *
      * @see TvView#timeShiftSetPlaybackParams(PlaybackParams)
-     * @hide
+     * @param params The new {@link PlaybackParams} that was set or changed.
      */
     public void notifyTimeShiftPlaybackParams(@NonNull PlaybackParams params) {
         if (DEBUG) {
@@ -760,7 +762,15 @@
      *
      * @see TvView.TvInputCallback#onTimeShiftStatusChanged(String, int)
      * @see android.media.tv.TvInputService.Session#notifyTimeShiftStatusChanged(int)
-     * @hide
+     * @param inputId The ID of the input for which the time shift status has changed.
+     * @param status The status of which the input has changed to. Should be one of the
+     *               following.
+     *               <ul>
+     *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNKNOWN}
+     *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNSUPPORTED}
+     *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_UNAVAILABLE}
+     *                  <li>{@link TvInputManager#TIME_SHIFT_STATUS_AVAILABLE}
+     *               </ul>
      */
     public void notifyTimeShiftStatusChanged(
             @NonNull String inputId, @TvInputManager.TimeShiftStatus int status) {
@@ -778,7 +788,8 @@
      * start position is changed.
      *
      * @see TvView.TimeShiftPositionCallback#onTimeShiftStartPositionChanged(String, long)
-     * @hide
+     * @param inputId The ID of the input for which the time shift start position has changed.
+     * @param timeMs The start position for time shifting, in milliseconds since the epoch.
      */
     public void notifyTimeShiftStartPositionChanged(@NonNull String inputId, long timeMs) {
         if (DEBUG) {
@@ -795,7 +806,8 @@
      * current position is changed.
      *
      * @see TvView.TimeShiftPositionCallback#onTimeShiftCurrentPositionChanged(String, long)
-     * @hide
+     * @param inputId The ID of the input for which the time shift current position has changed.
+     * @param timeMs The current position for time shifting, in milliseconds since the epoch.
      */
     public void notifyTimeShiftCurrentPositionChanged(@NonNull String inputId, long timeMs) {
         if (DEBUG) {
@@ -1068,7 +1080,6 @@
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
          * @param cmdType type of the command
          * @param parameters parameters of the command
-         * @hide
          */
         public void onTimeShiftCommandRequest(
                 @NonNull String iAppServiceId,
@@ -1186,7 +1197,6 @@
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @hide
          */
         public void onRequestTimeShiftMode(@NonNull String iAppServiceId) {
         }
@@ -1196,7 +1206,6 @@
          * called.
          *
          * @param iAppServiceId The ID of the TV interactive app service bound to this view.
-         * @hide
          */
         public void onRequestAvailableSpeeds(@NonNull String iAppServiceId) {
         }
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 4344e94..95b49a8 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -20,6 +20,9 @@
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupManagerMonitor;
+import android.app.backup.BackupRestoreEventLogger;
+import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
 import android.app.backup.BackupTransport;
 import android.app.backup.RestoreDescription;
 import android.app.backup.RestoreSet;
@@ -27,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -100,6 +104,7 @@
     private FileInputStream mCurFullRestoreStream;
     private byte[] mFullRestoreBuffer;
     private final LocalTransportParameters mParameters;
+    private final BackupManagerMonitor mMonitor = new TestBackupManagerMonitor();
 
     private void makeDataDirs() {
         mDataDir = mContext.getFilesDir();
@@ -887,4 +892,41 @@
     public long getBackupQuota(String packageName, boolean isFullBackup) {
         return isFullBackup ? FULL_BACKUP_SIZE_QUOTA : KEY_VALUE_BACKUP_SIZE_QUOTA;
     }
+
+    @Override
+    public BackupManagerMonitor getBackupManagerMonitor() {
+        return mMonitor;
+    }
+
+    private class TestBackupManagerMonitor extends BackupManagerMonitor {
+        @Override
+        public void onEvent(Bundle event) {
+            if (event == null || !mParameters.logAgentResults()) {
+                return;
+            }
+
+            if (event.getInt(BackupManagerMonitor.EXTRA_LOG_EVENT_ID)
+                    == BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS) {
+                Log.i(TAG, "agent_logging_results {");
+                ArrayList<DataTypeResult> results = event.getParcelableArrayList(
+                        BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS,
+                        DataTypeResult.class);
+                for (DataTypeResult result : results) {
+                    Log.i(TAG, "\tdataType: " + result.getDataType());
+                    Log.i(TAG, "\tsuccessCount: " + result.getSuccessCount());
+                    Log.i(TAG, "\tfailCount: " + result.getFailCount());
+
+                    if (!result.getErrors().isEmpty()) {
+                        Log.i(TAG, "\terrors {");
+                        for (String error : result.getErrors().keySet()) {
+                            Log.i(TAG, "\t\t" + error + ": " + result.getErrors().get(error));
+                        }
+                        Log.i(TAG, "\t}");
+                    }
+
+                    Log.i(TAG, "}");
+                }
+            }
+        }
+    }
 }
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
index 1ba1bc6..aaa18bf 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransportParameters.java
@@ -29,11 +29,13 @@
     private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
     private static final String KEY_IS_DEVICE_TRANSFER = "is_device_transfer";
     private static final String KEY_IS_ENCRYPTED = "is_encrypted";
+    private static final String KEY_LOG_AGENT_RESULTS = "log_agent_results";
 
     private boolean mFakeEncryptionFlag;
     private boolean mIsNonIncrementalOnly;
     private boolean mIsDeviceTransfer;
     private boolean mIsEncrypted;
+    private boolean mLogAgentResults;
 
     public LocalTransportParameters(Handler handler, ContentResolver resolver) {
         super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -55,6 +57,10 @@
         return mIsEncrypted;
     }
 
+    boolean logAgentResults() {
+        return mLogAgentResults;
+    }
+
     public String getSettingValue(ContentResolver resolver) {
         return Settings.Secure.getString(resolver, SETTING);
     }
@@ -64,5 +70,6 @@
         mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
         mIsDeviceTransfer = parser.getBoolean(KEY_IS_DEVICE_TRANSFER, false);
         mIsEncrypted = parser.getBoolean(KEY_IS_ENCRYPTED, false);
+        mLogAgentResults = parser.getBoolean(KEY_LOG_AGENT_RESULTS, /* def */ false);
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index f1b1abd..621e6ea 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -119,22 +119,22 @@
                 arguments = spp.parameter,
                 enterTransition = {
                     slideIntoContainer(
-                        AnimatedContentScope.SlideDirection.Left, animationSpec = slideEffect
+                        AnimatedContentScope.SlideDirection.Start, animationSpec = slideEffect
                     ) + fadeIn(animationSpec = fadeEffect)
                 },
                 exitTransition = {
                     slideOutOfContainer(
-                        AnimatedContentScope.SlideDirection.Left, animationSpec = slideEffect
+                        AnimatedContentScope.SlideDirection.Start, animationSpec = slideEffect
                     ) + fadeOut(animationSpec = fadeEffect)
                 },
                 popEnterTransition = {
                     slideIntoContainer(
-                        AnimatedContentScope.SlideDirection.Right, animationSpec = slideEffect
+                        AnimatedContentScope.SlideDirection.End, animationSpec = slideEffect
                     ) + fadeIn(animationSpec = fadeEffect)
                 },
                 popExitTransition = {
                     slideOutOfContainer(
-                        AnimatedContentScope.SlideDirection.Right, animationSpec = slideEffect
+                        AnimatedContentScope.SlideDirection.End, animationSpec = slideEffect
                     ) + fadeOut(animationSpec = fadeEffect)
                 },
             ) { navBackStackEntry ->
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
index 0137572..57bb838 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
@@ -201,11 +201,7 @@
         transition.AnimatedContent(
             modifier,
             transitionSpec = {
-                val zIndex = if (composeNavigator.isPop.value) {
-                    visibleEntries.indexOf(initialState).toFloat()
-                } else {
-                    visibleEntries.indexOf(targetState).toFloat()
-                }
+                val zIndex = composeNavigator.backStack.value.size.toFloat()
                 // If the initialState of the AnimatedContent is not in visibleEntries, we are in
                 // a case where visible has cleared the old state for some reason, so instead of
                 // attempting to animate away from the initialState, we skip the animation.
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 4a8c00e..f4a6b59 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -159,7 +159,7 @@
 }
 
 private fun <T : AppRecord> T.itemKey(option: Int) =
-    listOf(option, app.packageName, app.userId, System.identityHashCode(this))
+    listOf(option, app.packageName, app.userId)
 
 /** Returns group title if this is the first item of the group. */
 private fun <T : AppRecord> AppListModel<T>.getGroupTitleIfFirst(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
index 452971b..ebd82d9 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
@@ -2,7 +2,6 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.State
-import androidx.compose.runtime.remember
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
 import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
@@ -16,14 +15,12 @@
     onCheckedChange: ((newChecked: Boolean) -> Unit)?,
 ) {
     TwoTargetSwitchPreference(
-        model = remember {
-            object : SwitchPreferenceModel {
-                override val title = label
-                override val summary = this@AppListSwitchItem.summary
-                override val checked = checked
-                override val changeable = changeable
-                override val onCheckedChange = onCheckedChange
-            }
+        model = object : SwitchPreferenceModel {
+            override val title = label
+            override val summary = this@AppListSwitchItem.summary
+            override val checked = checked
+            override val changeable = changeable
+            override val onCheckedChange = onCheckedChange
         },
         icon = { AppIcon(record.app, SettingsDimension.appIconItemSize) },
         onClick = onClick,
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 00a58f2..7306a1f 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -116,6 +116,36 @@
         <item>full</item>
     </string-array>
 
+    <!-- Titles for Bluetooth HCI Snoop Filtered Logging -->
+    <string-array name="bt_hci_snoop_log_filters_entries">
+        <item>Headers Filtered</item>
+        <item>A2DP Media Packets Filtered</item>
+        <item>RFCOMM Channel Filtered</item>
+    </string-array>
+
+        <!-- Values for Bluetooth HCI Snoop Filtered Logging -->
+    <string-array name="bt_hci_snoop_log_filters_values" translatable="false">
+        <item>headers</item>
+        <item>profiles.a2dp</item>
+        <item>profiles.rfcomm</item>
+    </string-array>
+
+        <!-- Titles for Bluetooth HCI Snoop Filtered Logging -->
+    <string-array name="bt_hci_snoop_log_profile_filter_entries">
+        <item>Disabled</item>
+        <item>Magic</item>
+        <item>Header</item>
+        <item>Full Filter</item>
+    </string-array>
+
+        <!-- Values for Bluetooth HCI Snoop Filtered Logging -->
+    <string-array name="bt_hci_snoop_log_profile_filter_values" translatable="false">
+        <item>disabled</item>
+        <item>magic</item>
+        <item>header</item>
+        <item>fullfilter</item>
+    </string-array>
+
     <!-- Titles for Bluetooth AVRCP Versions -->
     <string-array name="bluetooth_avrcp_versions">
         <item>AVRCP 1.5 (Default)</item>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
index f83ebaa..720d6dd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java
@@ -26,16 +26,23 @@
 import android.content.IContentProvider;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.provider.DeviceConfig;
+import android.provider.DeviceConfigShellCommandHandler;
 import android.provider.Settings;
 import android.provider.Settings.Config.SyncDisabledMode;
+import android.provider.UpdatableDeviceConfigServiceReadiness;
+
+import com.android.internal.util.FastPrintWriter;
 
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -56,8 +63,32 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
-        (new MyShellCommand(mProvider)).exec(this, in, out, err, args, callback, resultReceiver);
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+            throws RemoteException {
+        if (UpdatableDeviceConfigServiceReadiness.shouldStartUpdatableService()) {
+            callUpdableDeviceConfigShellCommandHandler(in, out, err, args, resultReceiver);
+        } else {
+            (new MyShellCommand(mProvider))
+                    .exec(this, in, out, err, args, callback, resultReceiver);
+        }
+    }
+
+    private void callUpdableDeviceConfigShellCommandHandler(FileDescriptor in, FileDescriptor out,
+            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+        int result = -1;
+        try (
+                ParcelFileDescriptor inPfd = ParcelFileDescriptor.dup(in);
+                ParcelFileDescriptor outPfd = ParcelFileDescriptor.dup(out);
+                ParcelFileDescriptor errPfd = ParcelFileDescriptor.dup(err)) {
+            result = DeviceConfigShellCommandHandler.handleShellCommand(inPfd, outPfd, errPfd,
+                    args);
+        } catch (IOException e) {
+            PrintWriter pw = new FastPrintWriter(new FileOutputStream(err));
+            pw.println("dup() failed: " + e.getMessage());
+            pw.flush();
+        } finally {
+            resultReceiver.send(result, null);
+        }
     }
 
     static final class MyShellCommand extends ShellCommand {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
index fdab749..a08b598 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -23,6 +23,7 @@
 import android.graphics.Canvas
 import android.graphics.Typeface
 import android.graphics.fonts.Font
+import android.graphics.fonts.FontVariationAxis
 import android.text.Layout
 import android.util.SparseArray
 
@@ -215,13 +216,40 @@
             textInterpolator.targetPaint.textSize = textSize
         }
         if (weight >= 0) {
-            // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the
-            // memory impact, cache the typeface result.
-            textInterpolator.targetPaint.typeface =
-                typefaceCache.getOrElse(weight) {
-                    textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
-                    textInterpolator.targetPaint.typeface
+            val fontVariationArray =
+                    FontVariationAxis.fromFontVariationSettings(
+                        textInterpolator.targetPaint.fontVariationSettings
+                    )
+            if (fontVariationArray.isNullOrEmpty()) {
+                textInterpolator.targetPaint.typeface =
+                    typefaceCache.getOrElse(weight) {
+                        textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
+                        textInterpolator.targetPaint.typeface
+                    }
+            } else {
+                val idx = fontVariationArray.indexOfFirst { it.tag == "$TAG_WGHT" }
+                if (idx == -1) {
+                    val updatedFontVariation =
+                        textInterpolator.targetPaint.fontVariationSettings + ",'$TAG_WGHT' $weight"
+                    textInterpolator.targetPaint.typeface =
+                        typefaceCache.getOrElse(weight) {
+                            textInterpolator.targetPaint.fontVariationSettings =
+                                    updatedFontVariation
+                            textInterpolator.targetPaint.typeface
+                        }
+                } else {
+                    fontVariationArray[idx] = FontVariationAxis(
+                            "$TAG_WGHT", weight.toFloat())
+                    val updatedFontVariation =
+                            FontVariationAxis.toFontVariationSettings(fontVariationArray)
+                    textInterpolator.targetPaint.typeface =
+                        typefaceCache.getOrElse(weight) {
+                            textInterpolator.targetPaint.fontVariationSettings =
+                                    updatedFontVariation
+                            textInterpolator.targetPaint.typeface
+                        }
                 }
+            }
         }
         if (color != null) {
             textInterpolator.targetPaint.color = color
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
index 7bbfec7..a1d9d90 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableImageView.kt
@@ -15,7 +15,7 @@
  *
  */
 
-package com.android.systemui.common.ui.view
+package com.android.systemui.animation.view
 
 import android.content.Context
 import android.util.AttributeSet
@@ -23,6 +23,7 @@
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 
+/** An [ImageView] that also implements [LaunchableView]. */
 class LaunchableImageView : ImageView, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
index 2edac52..bce2622 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableLinearLayout.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableLinearLayout.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.common.ui.view
+package com.android.systemui.animation.view
 
 import android.content.Context
 import android.util.AttributeSet
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
similarity index 78%
copy from packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
copy to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
index 7bbfec7..286996d 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LaunchableImageView.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableTextView.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -15,15 +15,16 @@
  *
  */
 
-package com.android.systemui.common.ui.view
+package com.android.systemui.animation.view
 
 import android.content.Context
 import android.util.AttributeSet
-import android.widget.ImageView
+import android.widget.TextView
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.animation.LaunchableViewDelegate
 
-class LaunchableImageView : ImageView, LaunchableView {
+/** A [TextView] that also implements [LaunchableView]. */
+class LaunchableTextView : TextView, LaunchableView {
     private val delegate =
         LaunchableViewDelegate(
             this,
@@ -38,13 +39,6 @@
         defStyleAttr: Int,
     ) : super(context, attrs, defStyleAttr)
 
-    constructor(
-        context: Context?,
-        attrs: AttributeSet?,
-        defStyleAttr: Int,
-        defStyleRes: Int,
-    ) : super(context, attrs, defStyleAttr, defStyleRes)
-
     override fun setShouldBlockVisibilityChanges(block: Boolean) {
         delegate.setShouldBlockVisibilityChanges(block)
     }
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
index fc18132..6fe7d39 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.systemui.common.ui.view.LaunchableLinearLayout
+<com.android.systemui.animation.view.LaunchableLinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="0dp"
     android:layout_height="@dimen/qs_security_footer_single_line_height"
@@ -63,4 +63,4 @@
         android:src="@*android:drawable/ic_chevron_end"
         android:autoMirrored="true"
         android:tint="?android:attr/textColorSecondary" />
-</com.android.systemui.common.ui.view.LaunchableLinearLayout>
\ No newline at end of file
+</com.android.systemui.animation.view.LaunchableLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_progress_activity.xml b/packages/SystemUI/res/drawable/ic_progress_activity.xml
new file mode 100644
index 0000000..abf0625
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_progress_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2023 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="48dp"
+    android:height="48dp"
+    android:viewportWidth="48"
+    android:viewportHeight="48"
+    android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M24,44Q19.8,44 16.15,42.45Q12.5,40.9 9.8,38.2Q7.1,35.5 5.55,31.85Q4,28.2 4,24Q4,19.8 5.55,16.15Q7.1,12.5 9.8,9.8Q12.5,7.1 16.15,5.55Q19.8,4 24,4Q24.6,4 25.05,4.45Q25.5,4.9 25.5,5.5Q25.5,6.1 25.05,6.55Q24.6,7 24,7Q16.95,7 11.975,11.975Q7,16.95 7,24Q7,31.05 11.975,36.025Q16.95,41 24,41Q31.05,41 36.025,36.025Q41,31.05 41,24Q41,23.4 41.45,22.95Q41.9,22.5 42.5,22.5Q43.1,22.5 43.55,22.95Q44,23.4 44,24Q44,28.2 42.45,31.85Q40.9,35.5 38.2,38.2Q35.5,40.9 31.85,42.45Q28.2,44 24,44Z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/chipbar.xml b/packages/SystemUI/res/layout/chipbar.xml
index 8cf4f4d..0ff944c 100644
--- a/packages/SystemUI/res/layout/chipbar.xml
+++ b/packages/SystemUI/res/layout/chipbar.xml
@@ -60,14 +60,13 @@
             />
 
         <!-- At most one of [loading, failure_icon, undo] will be visible at a time. -->
-        <ProgressBar
+        <ImageView
             android:id="@+id/loading"
-            android:indeterminate="true"
             android:layout_width="@dimen/media_ttt_status_icon_size"
             android:layout_height="@dimen/media_ttt_status_icon_size"
             android:layout_marginStart="@dimen/media_ttt_last_item_start_margin"
-            android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
-            style="?android:attr/progressBarStyleSmall"
+            android:src="@drawable/ic_progress_activity"
+            android:tint="?androidprv:attr/colorAccentPrimaryVariant"
             android:alpha="0.0"
             />
 
diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
index 446bb01..fb78b49 100644
--- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
@@ -20,7 +20,7 @@
     android:layout_width="wrap_content"
     android:paddingVertical="@dimen/dream_overlay_complication_home_controls_padding">
 
-    <com.android.systemui.common.ui.view.LaunchableImageView
+    <com.android.systemui.animation.view.LaunchableImageView
         android:id="@+id/home_controls_chip"
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 3f95515..2871cdf 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -59,7 +59,7 @@
 
     </LinearLayout>
 
-    <com.android.systemui.common.ui.view.LaunchableImageView
+    <com.android.systemui.animation.view.LaunchableImageView
         android:id="@+id/start_button"
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
@@ -72,7 +72,7 @@
         android:layout_marginBottom="@dimen/keyguard_affordance_vertical_offset"
         android:visibility="gone" />
 
-    <com.android.systemui.common.ui.view.LaunchableImageView
+    <com.android.systemui.animation.view.LaunchableImageView
         android:id="@+id/end_button"
         android:layout_height="@dimen/keyguard_affordance_fixed_height"
         android:layout_width="@dimen/keyguard_affordance_fixed_width"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index f2e114b..9d91419 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -106,7 +106,7 @@
         app:layout_constrainedWidth="true"
         app:layout_constraintWidth_min="@dimen/min_clickable_item_size"
         app:layout_constraintHeight_min="@dimen/min_clickable_item_size">
-        <com.android.systemui.common.ui.view.LaunchableLinearLayout
+        <com.android.systemui.animation.view.LaunchableLinearLayout
             android:id="@+id/media_seamless_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -135,7 +135,7 @@
                 android:textDirection="locale"
                 android:textSize="12sp"
                 android:lineHeight="16sp" />
-        </com.android.systemui.common.ui.view.LaunchableLinearLayout>
+        </com.android.systemui.animation.view.LaunchableLinearLayout>
     </LinearLayout>
 
     <!-- Song name -->
diff --git a/packages/SystemUI/res/layout/ongoing_call_chip.xml b/packages/SystemUI/res/layout/ongoing_call_chip.xml
index 18d231c..238fc84 100644
--- a/packages/SystemUI/res/layout/ongoing_call_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_call_chip.xml
@@ -23,7 +23,7 @@
     android:layout_gravity="center_vertical|start"
     android:layout_marginStart="5dp"
 >
-    <com.android.systemui.common.ui.view.LaunchableLinearLayout
+    <com.android.systemui.animation.view.LaunchableLinearLayout
         android:id="@+id/ongoing_call_chip_background"
         android:layout_width="wrap_content"
         android:layout_height="@dimen/ongoing_appops_chip_height"
@@ -55,5 +55,5 @@
             android:textColor="?android:attr/colorPrimary"
         />
 
-    </com.android.systemui.common.ui.view.LaunchableLinearLayout>
+    </com.android.systemui.animation.view.LaunchableLinearLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 7716fa9..12e5f19 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -659,7 +659,7 @@
         <item>26</item> <!-- MOUTH_COVERING_DETECTED -->
     </integer-array>
 
-    <!-- Which device wake-ups will trigger face auth. These values correspond with
+    <!-- Which device wake-ups will trigger passive auth. These values correspond with
          PowerManager#WakeReason. -->
     <integer-array name="config_face_auth_wake_up_triggers">
         <item>1</item> <!-- WAKE_REASON_POWER_BUTTON -->
@@ -668,6 +668,7 @@
         <item>7</item> <!-- WAKE_REASON_WAKE_MOTION -->
         <item>9</item> <!-- WAKE_REASON_LID -->
         <item>10</item> <!-- WAKE_REASON_DISPLAY_GROUP_ADDED -->
+        <item>12</item> <!-- WAKE_REASON_UNFOLD_DEVICE -->
         <item>15</item> <!-- WAKE_REASON_TAP -->
         <item>16</item> <!-- WAKE_REASON_LIFT -->
         <item>17</item> <!-- WAKE_REASON_BIOMETRIC -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
index 30156a0..5e71bbe 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SurfaceViewRequestReceiver.java
@@ -75,7 +75,7 @@
             DisplayManager dm = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
             mSurfaceControlViewHost = new SurfaceControlViewHost(context,
                     dm.getDisplay(SurfaceViewRequestUtils.getDisplayId(bundle)),
-                    windowlessWindowManager);
+                    windowlessWindowManager, "SurfaceViewRequestReceiver");
             WindowManager.LayoutParams layoutParams =
                     new WindowManager.LayoutParams(
                             viewSize.getWidth(),
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 1051de3..6139403 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -202,11 +202,8 @@
         mKeyguardSecurityContainerController.onPause();
     }
 
-    /**
-     * Reinflate the view flipper child view.
-     */
-    public void reinflateViewFlipper() {
-        mKeyguardSecurityContainerController.reinflateViewFlipper();
+    public void resetSecurityContainer() {
+        mKeyguardSecurityContainerController.reset();
     }
 
     /**
@@ -235,19 +232,23 @@
     /**
      * Starts the animation when the Keyguard gets shown.
      */
-    public void appear() {
+    public void appear(int statusBarHeight) {
         // We might still be collapsed and the view didn't have time to layout yet or still
         // be small, let's wait on the predraw to do the animation in that case.
-        mView.getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        mView.getViewTreeObserver().removeOnPreDrawListener(this);
-                        mKeyguardSecurityContainerController.startAppearAnimation();
-                        return true;
-                    }
-                });
-        mView.requestLayout();
+        if (mView.getHeight() != 0 && mView.getHeight() != statusBarHeight) {
+            mKeyguardSecurityContainerController.startAppearAnimation();
+        } else {
+            mView.getViewTreeObserver().addOnPreDrawListener(
+                    new ViewTreeObserver.OnPreDrawListener() {
+                        @Override
+                        public boolean onPreDraw() {
+                            mView.getViewTreeObserver().removeOnPreDrawListener(this);
+                            mKeyguardSecurityContainerController.startAppearAnimation();
+                            return true;
+                        }
+                    });
+            mView.requestLayout();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 8281c8b..20baa81 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -744,20 +744,17 @@
     }
 
     private void reloadColors() {
-        reinflateViewFlipper();
+        resetViewFlipper();
         mView.reloadColors();
     }
 
     /** Handles density or font scale changes. */
     private void onDensityOrFontScaleChanged() {
-        reinflateViewFlipper();
+        resetViewFlipper();
         mView.onDensityOrFontScaleChanged();
     }
 
-    /**
-     * Reinflate the view flipper child view.
-     */
-    public void reinflateViewFlipper() {
+    private void resetViewFlipper() {
         mSecurityViewFlipperController.clearViews();
         mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
                 mKeyguardSecurityCallback);
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 5135eed..b30a0e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -51,6 +51,7 @@
     private float mStartRadius;
     private float mEndRadius;
     private int mHeight;
+    private boolean mInitialized;
 
     private static final int EXPAND_ANIMATION_MS = 100;
     private static final int EXPAND_COLOR_ANIMATION_MS = 50;
@@ -97,6 +98,11 @@
         mEndRadius = height / 4f;
         mExpandAnimator.setFloatValues(mStartRadius, mEndRadius);
         mContractAnimator.setFloatValues(mEndRadius, mStartRadius);
+        // Set initial corner radius.
+        if (!mInitialized) {
+            mBackground.setCornerRadius(mStartRadius);
+            mInitialized = true;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index cbeff2b..cd5f149 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -266,6 +267,7 @@
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
             FeatureFlags featureFlags,
+            NotifPipelineFlags notifPipelineFlags,
             @Main Executor sysuiMainExecutor) {
         return Optional.ofNullable(BubblesManager.create(context,
                 bubblesOptional,
@@ -283,6 +285,7 @@
                 notifPipeline,
                 sysUiState,
                 featureFlags,
+                notifPipelineFlags,
                 sysuiMainExecutor));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index d5634e3..b75b6d8 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -514,9 +514,7 @@
     @JvmField val SCREENSHOT_APP_CLIPS = releasedFlag(1304, "screenshot_app_clips")
 
     // TODO(b/268484562): Tracking bug
-    @JvmField
-    val SCREENSHOT_METADATA_REFACTOR =
-        unreleasedFlag(1305, "screenshot_metadata_refactor", teamfood = true)
+    @JvmField val SCREENSHOT_METADATA_REFACTOR = releasedFlag(1305, "screenshot_metadata_refactor")
 
     // 1400 - columbus
     // TODO(b/254512756): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index bb0d260..9f09d53 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -22,6 +22,7 @@
 import android.window.OnBackAnimationCallback
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.policy.SystemBarUtils
 import com.android.keyguard.KeyguardHostViewController
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -97,14 +98,14 @@
                     viewModel.setBouncerViewDelegate(delegate)
                     launch {
                         viewModel.show.collect {
-                            // Reset Security Container entirely.
-                            hostViewController.reinflateViewFlipper()
                             hostViewController.showPromptReason(it.promptReason)
                             it.errorMessage?.let { errorMessage ->
                                 hostViewController.showErrorMessage(errorMessage)
                             }
                             hostViewController.showPrimarySecurityScreen()
-                            hostViewController.appear()
+                            hostViewController.appear(
+                                SystemBarUtils.getStatusBarHeight(view.context)
+                            )
                             hostViewController.onResume()
                         }
                     }
@@ -113,6 +114,7 @@
                         viewModel.hide.collect {
                             hostViewController.cancelDismissAction()
                             hostViewController.cleanUp()
+                            hostViewController.resetSecurityContainer()
                         }
                     }
 
@@ -158,6 +160,15 @@
                     }
 
                     launch {
+                        viewModel.isBouncerVisible
+                            .filter { !it }
+                            .collect {
+                                // Remove existing input for security reasons.
+                                hostViewController.resetSecurityContainer()
+                            }
+                    }
+
+                    launch {
                         viewModel.keyguardPosition.collect { position ->
                             hostViewController.updateKeyguardPosition(position)
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 403576c..d977f91 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -97,6 +97,7 @@
                     context,
                     displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)),
                     hostToken,
+                    "KeyguardPreviewRenderer"
                 )
             disposables.add(DisposableHandle { host.release() })
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
index 049321b..b318252 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationDismissibilityProviderImpl.kt
@@ -17,11 +17,10 @@
 package com.android.systemui.statusbar.notification.collection.provider
 
 import androidx.annotation.VisibleForTesting
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.util.asIndenting
 import com.android.systemui.util.withIncreasedIndent
@@ -29,7 +28,9 @@
 import javax.inject.Inject
 
 @SysUISingleton
-class NotificationDismissibilityProviderImpl @Inject constructor(dumpManager: DumpManager) :
+class NotificationDismissibilityProviderImpl
+@Inject
+constructor(private val notifPipelineFlags: NotifPipelineFlags, dumpManager: DumpManager) :
     NotificationDismissibilityProvider, Dumpable {
 
     init {
@@ -42,8 +43,7 @@
         private set
 
     override fun isDismissable(entry: NotificationEntry): Boolean {
-        // TODO(b/268380968): inject FlagResolver
-        return if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(ALLOW_DISMISS_ONGOING)) {
+        return if (notifPipelineFlags.allowDismissOngoing()) {
             entry.key !in nonDismissableEntryKeys
         } else {
             entry.legacyIsDismissableRecursive()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 13b3aca..27fe747 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -70,7 +70,15 @@
         buffer.log(TAG, DEBUG, {
             str1 = entry.logKey
         }, {
-            "No alerting: snoozed package: $str1"
+            "No heads up: snoozed package: $str1"
+        })
+    }
+
+    fun logHeadsUpPackageSnoozeBypassedHasFsi(entry: NotificationEntry) {
+        buffer.log(TAG, DEBUG, {
+            str1 = entry.logKey
+        }, {
+            "Heads up: package snooze bypassed because notification has full-screen intent: $str1"
         })
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 9bcf92d..afeb72f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.statusbar.StatusBarState.SHADE;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD;
 import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR;
+import static com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI;
 
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -87,7 +88,10 @@
         FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD(1236),
 
         @UiEvent(doc = "HUN suppressed for old when")
-        HUN_SUPPRESSED_OLD_WHEN(1237);
+        HUN_SUPPRESSED_OLD_WHEN(1237),
+
+        @UiEvent(doc = "HUN snooze bypassed for potentially suppressed FSI")
+        HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI(1269);
 
         private final int mId;
 
@@ -409,7 +413,15 @@
             return false;
         }
 
-        if (isSnoozedPackage(sbn)) {
+        final boolean isSnoozedPackage = isSnoozedPackage(sbn);
+        final boolean fsiRequiresKeyguard = mFlags.fullScreenIntentRequiresKeyguard();
+        final boolean hasFsi = sbn.getNotification().fullScreenIntent != null;
+
+        // Assume any notification with an FSI is time-sensitive (like an alarm or incoming call)
+        // and ignore whether HUNs have been snoozed for the package.
+        final boolean shouldBypassSnooze = fsiRequiresKeyguard && hasFsi;
+
+        if (isSnoozedPackage && !shouldBypassSnooze) {
             if (log) mLogger.logNoHeadsUpPackageSnoozed(entry);
             return false;
         }
@@ -447,6 +459,19 @@
                 return false;
             }
         }
+
+        if (isSnoozedPackage) {
+            if (log) {
+                mLogger.logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+                final int uid = entry.getSbn().getUid();
+                final String packageName = entry.getSbn().getPackageName();
+                mUiEventLogger.log(HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI, uid,
+                        packageName);
+            }
+
+            return true;
+        }
+
         if (log) mLogger.logHeadsUp(entry);
         return true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
index 2d80edb..270c592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
@@ -21,7 +21,7 @@
 import android.widget.ImageView
 import android.widget.TextView
 import com.android.systemui.R
-import com.android.systemui.common.ui.view.LaunchableLinearLayout
+import com.android.systemui.animation.view.LaunchableLinearLayout
 
 class StatusBarUserSwitcherContainer(
     context: Context?,
diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
index dde2a80..27cafb1 100644
--- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerStartable.kt
@@ -74,6 +74,7 @@
 
         stylusUsiPowerUi.init()
         stylusManager.registerCallback(this)
+        stylusManager.registerBatteryCallback(this)
         stylusManager.startListener()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 696134c..a20a5b2 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.temporarydisplay.chipbar
 
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
 import android.content.Context
 import android.graphics.Rect
 import android.os.PowerManager
@@ -27,11 +29,14 @@
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
+import android.widget.ImageView
 import android.widget.TextView
 import androidx.annotation.IdRes
+import androidx.annotation.VisibleForTesting
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
 import com.android.systemui.classifier.FalsingCollector
 import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
 import com.android.systemui.common.shared.model.Text.Companion.loadText
@@ -101,6 +106,15 @@
 
     private lateinit var parent: ChipbarRootView
 
+    /** The current loading information, or null we're not currently loading. */
+    @VisibleForTesting
+    internal var loadingDetails: LoadingDetails? = null
+        private set(value) {
+            // Always cancel the old one before updating
+            field?.animator?.cancel()
+            field = value
+        }
+
     override val windowLayoutParams =
         commonWindowLayoutParams.apply { gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL) }
 
@@ -143,8 +157,22 @@
 
         // ---- End item ----
         // Loading
-        currentView.requireViewById<View>(R.id.loading).visibility =
-            (newInfo.endItem == ChipbarEndItem.Loading).visibleIfTrue()
+        val isLoading = newInfo.endItem == ChipbarEndItem.Loading
+        val loadingView = currentView.requireViewById<ImageView>(R.id.loading)
+        loadingView.visibility = isLoading.visibleIfTrue()
+
+        if (isLoading) {
+            val currentLoadingDetails = loadingDetails
+            // Since there can be multiple chipbars, we need to check if the loading view is the
+            // same and possibly re-start the loading animation on the new view.
+            if (currentLoadingDetails == null || currentLoadingDetails.loadingView != loadingView) {
+                val newDetails = createLoadingDetails(loadingView)
+                newDetails.animator.start()
+                loadingDetails = newDetails
+            }
+        } else {
+            loadingDetails = null
+        }
 
         // Error
         currentView.requireViewById<View>(R.id.error).visibility =
@@ -223,12 +251,17 @@
     override fun animateViewOut(view: ViewGroup, removalReason: String?, onAnimationEnd: Runnable) {
         val innerView = view.getInnerView()
         innerView.accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_NONE
-        val removed = chipbarAnimator.animateViewOut(innerView, onAnimationEnd)
+
+        val fullEndRunnable = Runnable {
+            loadingDetails = null
+            onAnimationEnd.run()
+        }
+        val removed = chipbarAnimator.animateViewOut(innerView, fullEndRunnable)
         // If the view doesn't get animated, the [onAnimationEnd] runnable won't get run. So, just
         // run it immediately.
         if (!removed) {
             logger.logAnimateOutFailure()
-            onAnimationEnd.run()
+            fullEndRunnable.run()
         }
 
         updateGestureListening()
@@ -269,7 +302,7 @@
     }
 
     private fun ViewGroup.getInnerView(): ViewGroup {
-        return requireViewById(R.id.chipbar_inner)
+        return this.requireViewById(R.id.chipbar_inner)
     }
 
     override fun getTouchableRegion(view: View, outRect: Rect) {
@@ -283,8 +316,28 @@
             View.GONE
         }
     }
+
+    private fun createLoadingDetails(loadingView: View): LoadingDetails {
+        // Ideally, we would use a <ProgressBar> view, which would automatically handle the loading
+        // spinner rotation for us. However, due to b/243983980, the ProgressBar animation
+        // unexpectedly pauses when SysUI starts another window. ObjectAnimator is a workaround that
+        // won't pause.
+        val animator =
+            ObjectAnimator.ofFloat(loadingView, View.ROTATION, 0f, 360f).apply {
+                duration = LOADING_ANIMATION_DURATION_MS
+                repeatCount = ValueAnimator.INFINITE
+                interpolator = Interpolators.LINEAR
+            }
+        return LoadingDetails(loadingView, animator)
+    }
+
+    internal data class LoadingDetails(
+        val loadingView: View,
+        val animator: ObjectAnimator,
+    )
 }
 
 @IdRes private val INFO_TAG = R.id.tag_chipbar_info
 private const val SWIPE_UP_GESTURE_REASON = "SWIPE_UP_GESTURE_DETECTED"
 private const val TAG = "ChipbarCoordinator"
+private const val LOADING_ANIMATION_DURATION_MS = 1000L
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 19a0866..6ef828f 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -166,7 +166,8 @@
 
         overlayAddReason = reason
 
-        val newRoot = SurfaceControlViewHost(context, context.display!!, wwm)
+        val newRoot = SurfaceControlViewHost(context, context.display!!, wwm,
+                "UnfoldLightRevealOverlayAnimation")
         val params = getLayoutParams()
         val newView =
             LightRevealScrim(
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserModule.java b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
index 2b29885..f7c8bac 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserModule.java
+++ b/packages/SystemUI/src/com/android/systemui/user/UserModule.java
@@ -21,6 +21,7 @@
 
 import com.android.settingslib.users.EditUserInfoController;
 import com.android.systemui.user.data.repository.UserRepositoryModule;
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserModeModule;
 import com.android.systemui.user.ui.dialog.UserDialogModule;
 
 import dagger.Binds;
@@ -36,6 +37,7 @@
         includes = {
                 UserDialogModule.class,
                 UserRepositoryModule.class,
+                HeadlessSystemUserModeModule.class,
         }
 )
 public abstract class UserModule {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
index 0a07439..2f63f32 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt
@@ -148,7 +148,7 @@
                         withContext(backgroundDispatcher) {
                             manager.getUserInfo(lastSelectedNonGuestUserHandle)
                         }
-                    if (info != null && info.isEnabled && info.supportsSwitchToByUser()) {
+                    if (info != null && info.isEnabled && info.supportsSwitchTo()) {
                         newUserId = info.id
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt
new file mode 100644
index 0000000..756e6a1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserMode.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2023 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.user.domain.interactor
+
+import android.os.UserManager
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+interface HeadlessSystemUserMode {
+
+    fun isHeadlessSystemUserMode(): Boolean
+}
+
+@SysUISingleton
+class HeadlessSystemUserModeImpl @Inject constructor() : HeadlessSystemUserMode {
+    override fun isHeadlessSystemUserMode(): Boolean {
+        return UserManager.isHeadlessSystemUserMode()
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/IIntArrayConsumer.aidl b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
similarity index 71%
rename from telephony/java/com/android/internal/telephony/IIntArrayConsumer.aidl
rename to packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
index c208755..0efa2d8 100644
--- a/telephony/java/com/android/internal/telephony/IIntArrayConsumer.aidl
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/HeadlessSystemUserModeModule.kt
@@ -14,10 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony;
+package com.android.systemui.user.domain.interactor
 
-// Copies consumer pattern for an operation that requires an int array result from another
-// process to finish.
-oneway interface IIntArrayConsumer {
-    void accept(in int[] result);
-}
\ No newline at end of file
+import dagger.Binds
+
+@dagger.Module
+interface HeadlessSystemUserModeModule {
+
+    @Binds
+    fun bindIsHeadlessSystemUserMode(impl: HeadlessSystemUserModeImpl): HeadlessSystemUserMode
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index c8f98c3..433642b 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -89,6 +89,7 @@
     private val keyguardInteractor: KeyguardInteractor,
     private val featureFlags: FeatureFlags,
     private val manager: UserManager,
+    private val headlessSystemUserMode: HeadlessSystemUserMode,
     @Application private val applicationScope: CoroutineScope,
     telephonyInteractor: TelephonyInteractor,
     broadcastDispatcher: BroadcastDispatcher,
@@ -571,7 +572,10 @@
             actionType = action,
             isRestricted = isRestricted,
             isSwitchToEnabled =
-                canSwitchUsers(selectedUserId) &&
+                canSwitchUsers(
+                    selectedUserId = selectedUserId,
+                    isAction = true,
+                ) &&
                     // If the user is auto-created is must not be currently resetting.
                     !(isGuestUserAutoCreated && isGuestUserResetting),
         )
@@ -723,10 +727,32 @@
         }
     }
 
-    private suspend fun canSwitchUsers(selectedUserId: Int): Boolean {
-        return withContext(backgroundDispatcher) {
-            manager.getUserSwitchability(UserHandle.of(selectedUserId))
-        } == UserManager.SWITCHABILITY_STATUS_OK
+    private suspend fun canSwitchUsers(
+        selectedUserId: Int,
+        isAction: Boolean = false,
+    ): Boolean {
+        val isHeadlessSystemUserMode =
+            withContext(backgroundDispatcher) { headlessSystemUserMode.isHeadlessSystemUserMode() }
+        // Whether menu item should be active. True if item is a user or if any user has
+        // signed in since reboot or in all cases for non-headless system user mode.
+        val isItemEnabled = !isAction || !isHeadlessSystemUserMode || isAnyUserUnlocked()
+        return isItemEnabled &&
+            withContext(backgroundDispatcher) {
+                manager.getUserSwitchability(UserHandle.of(selectedUserId))
+            } == UserManager.SWITCHABILITY_STATUS_OK
+    }
+
+    private suspend fun isAnyUserUnlocked(): Boolean {
+        return manager
+            .getUsers(
+                /* excludePartial= */ true,
+                /* excludeDying= */ true,
+                /* excludePreCreated= */ true
+            )
+            .any { user ->
+                user.id != UserHandle.USER_SYSTEM &&
+                    withContext(backgroundDispatcher) { manager.isUserUnlocked(user.userHandle) }
+            }
     }
 
     @SuppressLint("UseCompatLoadingForDrawables")
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 0dae918..9600fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -25,7 +25,6 @@
 import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.ALLOW_DISMISS_ONGOING;
 import static com.android.systemui.flags.Flags.WM_BUBBLE_BAR;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -51,7 +50,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.flags.FeatureFlags;
@@ -60,6 +58,7 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationChannelHelper;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -106,6 +105,7 @@
     private final NotificationLockscreenUserManager mNotifUserManager;
     private final CommonNotifCollection mCommonNotifCollection;
     private final NotifPipeline mNotifPipeline;
+    private final NotifPipelineFlags mNotifPipelineFlags;
     private final Executor mSysuiMainExecutor;
 
     private final Bubbles.SysuiProxy mSysuiProxy;
@@ -134,6 +134,7 @@
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
             FeatureFlags featureFlags,
+            NotifPipelineFlags notifPipelineFlags,
             Executor sysuiMainExecutor) {
         if (bubblesOptional.isPresent()) {
             return new BubblesManager(context,
@@ -152,6 +153,7 @@
                     notifPipeline,
                     sysUiState,
                     featureFlags,
+                    notifPipelineFlags,
                     sysuiMainExecutor);
         } else {
             return null;
@@ -175,6 +177,7 @@
             NotifPipeline notifPipeline,
             SysUiState sysUiState,
             FeatureFlags featureFlags,
+            NotifPipelineFlags notifPipelineFlags,
             Executor sysuiMainExecutor) {
         mContext = context;
         mBubbles = bubbles;
@@ -187,6 +190,7 @@
         mNotifUserManager = notifUserManager;
         mCommonNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
+        mNotifPipelineFlags = notifPipelineFlags;
         mSysuiMainExecutor = sysuiMainExecutor;
 
         mBarService = statusBarService == null
@@ -618,15 +622,15 @@
         }
     }
 
-    static BubbleEntry notifToBubbleEntry(NotificationEntry e) {
+    @VisibleForTesting
+    BubbleEntry notifToBubbleEntry(NotificationEntry e) {
         return new BubbleEntry(e.getSbn(), e.getRanking(), isDismissableFromBubbles(e),
                 e.shouldSuppressNotificationDot(), e.shouldSuppressNotificationList(),
                 e.shouldSuppressPeek());
     }
 
-    private static boolean isDismissableFromBubbles(NotificationEntry e) {
-        // TODO(b/268380968): inject FlagResolver
-        if (SystemUiSystemPropertiesFlags.getResolver().isEnabled(ALLOW_DISMISS_ONGOING)) {
+    private boolean isDismissableFromBubbles(NotificationEntry e) {
+        if (mNotifPipelineFlags.allowDismissOngoing()) {
             // Bubbles are only accessible from the unlocked state,
             // so we can calculate this from the Notification flags only.
             return e.isDismissableForState(/*isLocked=*/ false);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 7b0d091d..0d65f12 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -602,14 +602,6 @@
                 any(KeyguardSecurityCallback.class));
     }
 
-    @Test
-    public void testReinflateViewFlipper() {
-        mKeyguardSecurityContainerController.reinflateViewFlipper();
-        verify(mKeyguardSecurityViewFlipperController).clearViews();
-        verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
-                any(KeyguardSecurityCallback.class));
-    }
-
     private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() {
         mKeyguardSecurityContainerController.onViewAttached();
         verify(mView).setSwipeListener(mSwipeListenerArgumentCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
new file mode 100644
index 0000000..9fcb9c8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/NumPadAnimatorTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 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.keyguard
+
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyFloat
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class NumPadAnimatorTest : SysuiTestCase() {
+    @Mock lateinit var background: GradientDrawable
+    @Mock lateinit var buttonImage: Drawable
+    private lateinit var underTest: NumPadAnimator
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        underTest = NumPadAnimator(context, background, 0, buttonImage)
+    }
+
+    @Test
+    fun testOnLayout() {
+        underTest.onLayout(100)
+        verify(background).cornerRadius = 50f
+        reset(background)
+        underTest.onLayout(100)
+        verify(background, never()).cornerRadius = anyFloat()
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
index d7aa6e0..b389558 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -19,6 +19,7 @@
 import android.animation.AnimatorListenerAdapter
 import android.animation.ValueAnimator
 import android.graphics.Typeface
+import android.graphics.fonts.FontVariationAxis
 import android.testing.AndroidTestingRunner
 import android.text.Layout
 import android.text.StaticLayout
@@ -179,4 +180,71 @@
 
         assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
     }
+
+    @Test
+    fun testSetTextStyle_addWeight() {
+        testWeightChange("", 100, FontVariationAxis.fromFontVariationSettings("'wght' 100")!!)
+    }
+
+    @Test
+    fun testSetTextStyle_changeWeight() {
+        testWeightChange(
+                "'wght' 500",
+                100,
+                FontVariationAxis.fromFontVariationSettings("'wght' 100")!!
+        )
+    }
+
+    @Test
+    fun testSetTextStyle_addWeightWithOtherAxis() {
+        testWeightChange(
+                "'wdth' 100",
+                100,
+                FontVariationAxis.fromFontVariationSettings("'wght' 100, 'wdth' 100")!!
+        )
+    }
+
+    @Test
+    fun testSetTextStyle_changeWeightWithOtherAxis() {
+        testWeightChange(
+                "'wght' 500, 'wdth' 100",
+                100,
+                FontVariationAxis.fromFontVariationSettings("'wght' 100, 'wdth' 100")!!
+        )
+    }
+
+    private fun testWeightChange(
+            initialFontVariationSettings: String,
+            weight: Int,
+            expectedFontVariationSettings: Array<FontVariationAxis>
+    ) {
+        val layout = makeLayout("Hello, World", PAINT)
+        val valueAnimator = mock(ValueAnimator::class.java)
+        val textInterpolator = mock(TextInterpolator::class.java)
+        val paint =
+                TextPaint().apply {
+                    typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
+                    fontVariationSettings = initialFontVariationSettings
+                }
+        `when`(textInterpolator.targetPaint).thenReturn(paint)
+
+        val textAnimator =
+                TextAnimator(layout, {}).apply {
+                    this.textInterpolator = textInterpolator
+                    this.animator = valueAnimator
+                }
+        textAnimator.setTextStyle(weight = weight, animate = false)
+
+        val resultFontVariationList =
+                FontVariationAxis.fromFontVariationSettings(
+                        textInterpolator.targetPaint.fontVariationSettings
+                )
+        expectedFontVariationSettings.forEach { expectedAxis ->
+            val resultAxis = resultFontVariationList?.filter { it.tag == expectedAxis.tag }?.get(0)
+            assertThat(resultAxis).isNotNull()
+            if (resultAxis != null) {
+                assertThat(resultAxis.styleValue).isEqualTo(expectedAxis.styleValue)
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
index fc1d38b..8534d4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamHomeControlsComplicationTest.java
@@ -37,7 +37,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.common.ui.view.LaunchableImageView;
+import com.android.systemui.animation.view.LaunchableImageView;
 import com.android.systemui.controls.ControlsServiceInfo;
 import com.android.systemui.controls.controller.ControlsController;
 import com.android.systemui.controls.controller.StructureInfo;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
index 55a79da..ed24947 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinatorTest.kt
@@ -19,10 +19,9 @@
 import android.app.Notification
 import android.testing.AndroidTestingRunner
 import androidx.test.filters.SmallTest
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.notification.NotifPipelineFlags
 import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -48,15 +47,14 @@
     private lateinit var onBeforeRenderListListener: OnBeforeRenderListListener
     private val keyguardStateController: KeyguardStateController = mock()
     private val pipeline: NotifPipeline = mock()
-    private val flagResolver: SystemUiSystemPropertiesFlags.FlagResolver = mock()
+    private val flags: NotifPipelineFlags = mock()
     private val dumpManager: DumpManager = mock()
 
     @Before
     fun setUp() {
-        setTestFlagResolver(flagResolver)
-        whenever(flagResolver.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)).thenReturn(true)
+        whenever(flags.allowDismissOngoing()).thenReturn(true)
 
-        dismissibilityProvider = NotificationDismissibilityProviderImpl(dumpManager)
+        dismissibilityProvider = NotificationDismissibilityProviderImpl(flags, dumpManager)
         coordinator = DismissibilityCoordinator(keyguardStateController, dismissibilityProvider)
         coordinator.attach(pipeline)
         onBeforeRenderListListener = withArgCaptor {
@@ -314,7 +312,7 @@
 
     @Test
     fun testFeatureToggleOffNonDismissibleEntry() {
-        whenever(flagResolver.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)).thenReturn(false)
+        whenever(flags.allowDismissOngoing()).thenReturn(false)
         val entry =
             NotificationEntryBuilder()
                 .setTag("entry")
@@ -331,7 +329,7 @@
 
     @Test
     fun testFeatureToggleOffOngoingNotifWhenPhoneIsLocked() {
-        whenever(flagResolver.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)).thenReturn(false)
+        whenever(flags.allowDismissOngoing()).thenReturn(false)
         whenever(keyguardStateController.isUnlocked).thenReturn(false)
         val entry =
             NotificationEntryBuilder()
@@ -349,7 +347,7 @@
 
     @Test
     fun testFeatureToggleOffOngoingNotifWhenPhoneIsUnLocked() {
-        whenever(flagResolver.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)).thenReturn(false)
+        whenever(flags.allowDismissOngoing()).thenReturn(false)
         whenever(keyguardStateController.isUnlocked).thenReturn(true)
         val entry =
             NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
index 831d07f..07d0dbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java
@@ -741,7 +741,7 @@
     }
 
     @Test
-    public void testShouldFullScreen_snoozed_occluding_withStrictRules() throws Exception {
+    public void testShouldNotFullScreen_snoozed_occluding_withStrictRules() throws Exception {
         when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -753,16 +753,41 @@
         when(mKeyguardStateController.isOccluded()).thenReturn(true);
 
         assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.FSI_KEYGUARD_OCCLUDED);
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
         assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
-                .isTrue();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
+                .isFalse();
+        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
         verify(mLogger, never()).logNoFullscreenWarning(any(), any());
-        verify(mLogger).logFullscreen(entry, "Expected not to HUN while keyguard occluded");
+        verify(mLogger, never()).logFullscreen(any(), any());
     }
 
     @Test
-    public void testShouldFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
+    public void testShouldHeadsUp_snoozed_occluding_withStrictRules() throws Exception {
+        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE);
+        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(true);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+        verify(mLogger, never()).logHeadsUp(any());
+
+        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
+        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
+        assertThat(fakeUiEvent.eventId).isEqualTo(
+                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
+        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
+        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
+    }
+
+    @Test
+    public void testShouldNotFullScreen_snoozed_lockedShade_withStrictRules() throws Exception {
         when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
         NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
         when(mPowerManager.isInteractive()).thenReturn(true);
@@ -774,12 +799,37 @@
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
 
         assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.FSI_LOCKED_SHADE);
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
         assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
-                .isTrue();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
+                .isFalse();
+        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
         verify(mLogger, never()).logNoFullscreenWarning(any(), any());
-        verify(mLogger).logFullscreen(entry, "Keyguard is showing and not occluded");
+        verify(mLogger, never()).logFullscreen(any(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_snoozed_lockedShade_withStrictRules() throws Exception {
+        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE_LOCKED);
+        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+        verify(mLogger, never()).logHeadsUp(any());
+
+        assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
+        UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
+        assertThat(fakeUiEvent.eventId).isEqualTo(
+                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
+        assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
+        assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
     }
 
     @Test
@@ -795,21 +845,41 @@
         when(mKeyguardStateController.isOccluded()).thenReturn(false);
 
         assertThat(mNotifInterruptionStateProvider.getFullScreenIntentDecision(entry))
-                .isEqualTo(FullScreenIntentDecision.NO_FSI_NO_HUN_OR_KEYGUARD);
+                .isEqualTo(FullScreenIntentDecision.NO_FSI_EXPECTED_TO_HUN);
         assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry))
                 .isFalse();
-        verify(mLogger, never()).logNoFullscreen(any(), any());
-        verify(mLogger).logNoFullscreenWarning(entry, "Expected not to HUN while not on keyguard");
+        verify(mLogger).logNoFullscreen(entry, "Expected to HUN");
+        verify(mLogger, never()).logNoFullscreenWarning(any(), any());
         verify(mLogger, never()).logFullscreen(any(), any());
+    }
+
+    @Test
+    public void testShouldHeadsUp_snoozed_unlocked_withStrictRules() throws Exception {
+        when(mFlags.fullScreenIntentRequiresKeyguard()).thenReturn(true);
+        NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false);
+        when(mPowerManager.isInteractive()).thenReturn(true);
+        when(mPowerManager.isScreenOn()).thenReturn(true);
+        when(mDreamManager.isDreaming()).thenReturn(false);
+        when(mStatusBarStateController.getState()).thenReturn(SHADE);
+        when(mHeadsUpManager.isSnoozed("a")).thenReturn(true);
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        when(mKeyguardStateController.isOccluded()).thenReturn(false);
+
+        assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isTrue();
+
+        verify(mLogger).logHeadsUpPackageSnoozeBypassedHasFsi(entry);
+        verify(mLogger, never()).logHeadsUp(any());
 
         assertThat(mUiEventLoggerFake.numLogs()).isEqualTo(1);
         UiEventLoggerFake.FakeUiEvent fakeUiEvent = mUiEventLoggerFake.get(0);
         assertThat(fakeUiEvent.eventId).isEqualTo(
-                NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD.getId());
+                NotificationInterruptEvent.HUN_SNOOZE_BYPASSED_POTENTIALLY_SUPPRESSED_FSI.getId());
         assertThat(fakeUiEvent.uid).isEqualTo(entry.getSbn().getUid());
         assertThat(fakeUiEvent.packageName).isEqualTo(entry.getSbn().getPackageName());
     }
 
+    /* TODO: Verify the FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD UiEvent some other way. */
+
     /**
      * Bubbles can happen.
      */
diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
index cc6be5e..82b80f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerStartableTest.kt
@@ -92,6 +92,14 @@
     }
 
     @Test
+    fun start_registersCallbacks() {
+        startable.start()
+
+        verify(stylusManager, times(1)).registerCallback(startable)
+        verify(stylusManager, times(1)).registerBatteryCallback(startable)
+    }
+
+    @Test
     fun onStylusAdded_internal_updatesNotificationSuppression() {
         startable.onStylusAdded(STYLUS_DEVICE_ID)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index fc7436a..586bdc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -27,6 +27,7 @@
 import android.view.accessibility.AccessibilityManager
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.core.animation.doOnCancel
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.R
@@ -361,6 +362,105 @@
     }
 
     @Test
+    fun displayView_loading_animationStarted() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        assertThat(underTest.loadingDetails!!.animator.isStarted).isTrue()
+    }
+
+    @Test
+    fun displayView_notLoading_noAnimation() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Error,
+            )
+        )
+
+        assertThat(underTest.loadingDetails).isNull()
+    }
+
+    @Test
+    fun displayView_loadingThenNotLoading_animationStopped() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val animator = underTest.loadingDetails!!.animator
+        var cancelled = false
+        animator.doOnCancel { cancelled = true }
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Button(Text.Loaded("button")) {},
+            )
+        )
+
+        assertThat(cancelled).isTrue()
+        assertThat(underTest.loadingDetails).isNull()
+    }
+
+    @Test
+    fun displayView_loadingThenHideView_animationStopped() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val animator = underTest.loadingDetails!!.animator
+        var cancelled = false
+        animator.doOnCancel { cancelled = true }
+
+        underTest.removeView(DEVICE_ID, "TestReason")
+
+        assertThat(cancelled).isTrue()
+        assertThat(underTest.loadingDetails).isNull()
+    }
+
+    @Test
+    fun displayView_loadingThenNewLoading_animationStaysTheSame() {
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        val animator = underTest.loadingDetails!!.animator
+        var cancelled = false
+        animator.doOnCancel { cancelled = true }
+
+        underTest.displayView(
+            createChipbarInfo(
+                Icon.Resource(R.id.check_box, null),
+                Text.Loaded("new text"),
+                endItem = ChipbarEndItem.Loading,
+            )
+        )
+
+        assertThat(underTest.loadingDetails!!.animator).isEqualTo(animator)
+        assertThat(underTest.loadingDetails!!.animator.isStarted).isTrue()
+        assertThat(cancelled).isFalse()
+    }
+
+    @Test
     fun displayView_vibrationEffect_doubleClickEffect() {
         underTest.displayView(
             createChipbarInfo(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
index cc23485..0c119fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/GuestUserInteractorTest.kt
@@ -384,14 +384,14 @@
             UserInfo(
                 /* id= */ 818,
                 /* name= */ "non_guest",
-                /* flags= */ 0,
+                /* flags= */ UserInfo.FLAG_FULL,
             )
         private val GUEST_USER_INFO =
             UserInfo(
                 /* id= */ 669,
                 /* name= */ "guest",
                 /* iconPath= */ "",
-                /* flags= */ 0,
+                /* flags= */ UserInfo.FLAG_FULL,
                 UserManager.USER_TYPE_FULL_GUEST,
             )
         private val EPHEMERAL_GUEST_USER_INFO =
@@ -399,7 +399,7 @@
                 /* id= */ 669,
                 /* name= */ "guest",
                 /* iconPath= */ "",
-                /* flags= */ UserInfo.FLAG_EPHEMERAL,
+                /* flags= */ UserInfo.FLAG_EPHEMERAL or UserInfo.FLAG_FULL,
                 UserManager.USER_TYPE_FULL_GUEST,
             )
         private val ALL_USERS =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 3538d9b..22d05bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -28,7 +28,6 @@
 import android.os.UserManager
 import android.provider.Settings
 import androidx.test.filters.SmallTest
-import com.android.internal.R.drawable.ic_account_circle
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.GuestResetOrExitSessionReceiver
 import com.android.systemui.GuestResumeSessionReceiver
@@ -89,6 +88,7 @@
 
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
@@ -147,6 +147,7 @@
                         featureFlags = featureFlags,
                     ),
                 manager = manager,
+                headlessSystemUserMode = headlessSystemUserMode,
                 applicationScope = testScope.backgroundScope,
                 telephonyInteractor =
                     TelephonyInteractor(
@@ -890,6 +891,50 @@
             assertThat(selectedUser()).isNotNull()
         }
 
+    @Test
+    fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled() =
+        testScope.runTest {
+            keyguardRepository.setKeyguardShowing(true)
+            whenever(manager.getUserSwitchability(any()))
+                .thenReturn(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED)
+            val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+            userRepository.setUserInfos(userInfos)
+            userRepository.setSelectedUserInfo(userInfos[1])
+            userRepository.setSettings(
+                UserSwitcherSettingsModel(
+                    isUserSwitcherEnabled = true,
+                    isAddUsersFromLockscreen = true
+                )
+            )
+
+            runCurrent()
+            underTest.userRecords.value
+                .filter { it.info == null }
+                .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
+        }
+
+    @Test
+    fun userRecords_isActionAndNoUsersUnlocked_actionIsDisabled_HeadlessMode() =
+        testScope.runTest {
+            keyguardRepository.setKeyguardShowing(true)
+            whenever(headlessSystemUserMode.isHeadlessSystemUserMode()).thenReturn(true)
+            whenever(manager.isUserUnlocked(anyInt())).thenReturn(false)
+            val userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+            userRepository.setUserInfos(userInfos)
+            userRepository.setSelectedUserInfo(userInfos[1])
+            userRepository.setSettings(
+                UserSwitcherSettingsModel(
+                    isUserSwitcherEnabled = true,
+                    isAddUsersFromLockscreen = true
+                )
+            )
+
+            runCurrent()
+            underTest.userRecords.value
+                .filter { it.info == null }
+                .forEach { action -> assertThat(action.isSwitchToEnabled).isFalse() }
+        }
+
     private fun assertUsers(
         models: List<UserModel>?,
         count: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 8f0375f..2f05e65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.util.mockito.mock
@@ -71,6 +72,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var uiEventLogger: UiEventLogger
@@ -252,6 +254,7 @@
                         ),
                     featureFlags = featureFlags,
                     manager = manager,
+                    headlessSystemUserMode = headlessSystemUserMode,
                     applicationScope = testScope.backgroundScope,
                     telephonyInteractor =
                         TelephonyInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 71a112c..9268904 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.user.data.model.UserSwitcherSettingsModel
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
 import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
 import com.android.systemui.user.domain.interactor.UserInteractor
 import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -72,6 +73,7 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var activityManager: ActivityManager
     @Mock private lateinit var manager: UserManager
+    @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
     @Mock private lateinit var uiEventLogger: UiEventLogger
@@ -154,6 +156,7 @@
                                 ),
                             featureFlags = featureFlags,
                             manager = manager,
+                            headlessSystemUserMode = headlessSystemUserMode,
                             applicationScope = testScope.backgroundScope,
                             telephonyInteractor =
                                 TelephonyInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 185d408..e185922 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -82,8 +82,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
 import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
@@ -285,7 +283,7 @@
     @Mock
     private ShadeWindowLogger mShadeWindowLogger;
     @Mock
-    private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+    private NotifPipelineFlags mNotifPipelineFlags;
 
     private TestableBubblePositioner mPositioner;
 
@@ -298,8 +296,6 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        setTestFlagResolver(mFlagResolver);
-
         mTestableLooper = TestableLooper.get(this);
 
         // For the purposes of this test, just run everything synchronously
@@ -318,24 +314,6 @@
         mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
         mNotificationShadeWindowController.attach();
 
-        // Need notifications for bubbles
-        mNotificationTestHelper = new NotificationTestHelper(
-                mContext,
-                mDependency,
-                TestableLooper.get(this));
-        mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
-        mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
-        mNonBubbleNotifRow = mNotificationTestHelper.createRow();
-        mBubbleEntry = BubblesManager.notifToBubbleEntry(mRow);
-        mBubbleEntry2 = BubblesManager.notifToBubbleEntry(mRow2);
-
-        UserHandle handle = mock(UserHandle.class);
-        when(handle.getIdentifier()).thenReturn(11);
-        mBubbleEntryUser11 = BubblesManager.notifToBubbleEntry(
-                mNotificationTestHelper.createBubble(handle));
-        mBubbleEntry2User11 = BubblesManager.notifToBubbleEntry(
-                mNotificationTestHelper.createBubble(handle));
-
         mAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
         mAppBubbleIntent.setPackage(mContext.getPackageName());
 
@@ -420,9 +398,28 @@
                 mNotifPipeline,
                 mSysUiState,
                 mock(FeatureFlags.class),
+                mNotifPipelineFlags,
                 syncExecutor);
         mBubblesManager.addNotifCallback(mNotifCallback);
 
+        // Need notifications for bubbles
+        mNotificationTestHelper = new NotificationTestHelper(
+                mContext,
+                mDependency,
+                TestableLooper.get(this));
+        mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
+        mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
+        mNonBubbleNotifRow = mNotificationTestHelper.createRow();
+        mBubbleEntry = mBubblesManager.notifToBubbleEntry(mRow);
+        mBubbleEntry2 = mBubblesManager.notifToBubbleEntry(mRow2);
+
+        UserHandle handle = mock(UserHandle.class);
+        when(handle.getIdentifier()).thenReturn(11);
+        mBubbleEntryUser11 = mBubblesManager.notifToBubbleEntry(
+                mNotificationTestHelper.createBubble(handle));
+        mBubbleEntry2User11 = mBubblesManager.notifToBubbleEntry(
+                mNotificationTestHelper.createBubble(handle));
+
         // Get a reference to the BubbleController's entry listener
         verify(mNotifPipeline, atLeastOnce())
                 .addCollectionListener(mNotifListenerCaptor.capture());
@@ -1185,7 +1182,7 @@
     @Test
     public void testDeleteShortcutsDeletesXml() throws Exception {
         ExpandableNotificationRow row = mNotificationTestHelper.createShortcutBubble("shortcutId");
-        BubbleEntry shortcutBubbleEntry = BubblesManager.notifToBubbleEntry(row.getEntry());
+        BubbleEntry shortcutBubbleEntry = mBubblesManager.notifToBubbleEntry(row.getEntry());
         mBubbleController.updateBubble(shortcutBubbleEntry);
 
         mBubbleData.dismissBubbleWithKey(shortcutBubbleEntry.getKey(),
@@ -1297,7 +1294,7 @@
         entry.getChannel().setConversationId(
                 row.getEntry().getChannel().getParentChannelId(),
                 "shortcutId");
-        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+        mBubbleController.updateBubble(mBubblesManager.notifToBubbleEntry(row.getEntry()));
         assertTrue(mBubbleController.hasBubbles());
 
         // Overflow it
@@ -1323,7 +1320,7 @@
         entry.getChannel().setConversationId(
                 row.getEntry().getChannel().getParentChannelId(),
                 "shortcutId");
-        mBubbleController.updateBubble(BubblesManager.notifToBubbleEntry(row.getEntry()));
+        mBubbleController.updateBubble(mBubblesManager.notifToBubbleEntry(row.getEntry()));
         assertTrue(mBubbleController.hasBubbles());
 
         // Overflow it
@@ -1706,13 +1703,13 @@
 
     @Test
     public void testCreateBubbleFromOngoingNotification_OngoingDismissalEnabled() {
-        when(mFlagResolver.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)).thenReturn(true);
+        when(mNotifPipelineFlags.allowDismissOngoing()).thenReturn(true);
         NotificationEntry notif = new NotificationEntryBuilder()
                 .setFlag(mContext, Notification.FLAG_ONGOING_EVENT, true)
                 .setCanBubble(true)
                 .build();
 
-        BubbleEntry bubble = BubblesManager.notifToBubbleEntry(notif);
+        BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
 
         assertTrue("Ongoing Notifis should be dismissable", bubble.isDismissable());
     }
@@ -1720,13 +1717,13 @@
 
     @Test
     public void testCreateBubbleFromNoDismissNotification_OngoingDismissalEnabled() {
-        when(mFlagResolver.isEnabled(NotificationFlags.ALLOW_DISMISS_ONGOING)).thenReturn(true);
+        when(mNotifPipelineFlags.allowDismissOngoing()).thenReturn(true);
         NotificationEntry notif = new NotificationEntryBuilder()
                 .setFlag(mContext, Notification.FLAG_NO_DISMISS, true)
                 .setCanBubble(true)
                 .build();
 
-        BubbleEntry bubble = BubblesManager.notifToBubbleEntry(notif);
+        BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
 
         assertFalse("FLAG_NO_DISMISS Notifs should be non-dismissable", bubble.isDismissable());
     }
@@ -1738,7 +1735,7 @@
                 .setCanBubble(true)
                 .build();
 
-        BubbleEntry bubble = BubblesManager.notifToBubbleEntry(notif);
+        BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
 
         assertFalse(
                 "Ongoing Notifis should be dismissable, if the feature is off",
@@ -1754,7 +1751,7 @@
                 .setCanBubble(true)
                 .build();
 
-        BubbleEntry bubble = BubblesManager.notifToBubbleEntry(notif);
+        BubbleEntry bubble = mBubblesManager.notifToBubbleEntry(notif);
 
         assertTrue(
                 "FLAG_NO_DISMISS should be ignored, if the feature is off",
@@ -1772,7 +1769,7 @@
         workEntry.setBubbleMetadata(getMetadata());
         workEntry.setFlagBubble(true);
 
-        return new Bubble(BubblesManager.notifToBubbleEntry(workEntry),
+        return new Bubble(mBubblesManager.notifToBubbleEntry(workEntry),
                 null,
                 mock(Bubbles.PendingIntentCanceledListener.class), new SyncExecutor());
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index c236bc9..1bab997 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -34,7 +34,6 @@
 import androidx.test.InstrumentationRegistry;
 import androidx.test.uiautomator.UiDevice;
 
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.animation.DialogLaunchAnimator;
@@ -145,7 +144,6 @@
         disallowTestableLooperAsMainThread();
         mContext.cleanUpReceivers(this.getClass().getSimpleName());
         mFakeBroadcastDispatcher.cleanUpReceivers(this.getClass().getSimpleName());
-        setTestFlagResolver(null);
     }
 
     @AfterClass
@@ -208,10 +206,6 @@
         }
     }
 
-    protected void setTestFlagResolver(SystemUiSystemPropertiesFlags.FlagResolver flagResolver) {
-        SystemUiSystemPropertiesFlags.TEST_RESOLVER = flagResolver;
-    }
-
     public static void waitForIdleSync(Handler h) {
         validateThread(h.getLooper());
         Idler idler = new Idler(null);
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 24d7c90..598521f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1458,7 +1458,7 @@
         if (((response.getDatasets() == null || response.getDatasets().isEmpty())
                         && response.getAuthentication() == null)
                 || autofillDisabled) {
-            // Response is "empty" from an UI point of view, need to notify client.
+            // Response is "empty" from a UI point of view, need to notify client.
             notifyUnavailableToClient(
                     autofillDisabled ? AutofillManager.STATE_DISABLED_BY_SERVICE : 0,
                     /* autofillableIds= */ null);
@@ -4283,7 +4283,7 @@
 
         mService.resetLastResponse();
 
-        // The default autofill service cannot fullfill the request, let's check if the augmented
+        // The default autofill service cannot fulfill the request, let's check if the augmented
         // autofill service can.
         mAugmentedAutofillDestroyer = triggerAugmentedAutofillLocked(flags);
         if (mAugmentedAutofillDestroyer == null && ((flags & FLAG_PASSWORD_INPUT_TYPE) == 0)) {
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index ec7e993..7804ebf 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -17,7 +17,9 @@
 package com.android.server.companion.virtual;
 
 import android.annotation.NonNull;
-import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback;
+import android.annotation.Nullable;
+import android.companion.virtual.sensor.IVirtualSensorCallback;
+import android.companion.virtual.sensor.VirtualSensor;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.os.IBinder;
@@ -40,17 +42,30 @@
 
     private static final String TAG = "SensorController";
 
+    // See system/core/libutils/include/utils/Errors.h
+    private static final int OK = 0;
+    private static final int UNKNOWN_ERROR = (-2147483647 - 1); // INT32_MIN value
+    private static final int BAD_VALUE = -22;
+
     private final Object mLock;
     private final int mVirtualDeviceId;
     @GuardedBy("mLock")
     private final Map<IBinder, SensorDescriptor> mSensorDescriptors = new ArrayMap<>();
 
+    @NonNull
+    private final SensorManagerInternal.RuntimeSensorCallback mRuntimeSensorCallback;
     private final SensorManagerInternal mSensorManagerInternal;
+    private final VirtualDeviceManagerInternal mVdmInternal;
 
-    public SensorController(@NonNull Object lock, int virtualDeviceId) {
+
+
+    public SensorController(@NonNull Object lock, int virtualDeviceId,
+            @Nullable IVirtualSensorCallback virtualSensorCallback) {
         mLock = lock;
         mVirtualDeviceId = virtualDeviceId;
+        mRuntimeSensorCallback = new RuntimeSensorCallbackWrapper(virtualSensorCallback);
         mSensorManagerInternal = LocalServices.getService(SensorManagerInternal.class);
+        mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
     }
 
     void close() {
@@ -67,36 +82,23 @@
         }
     }
 
-    void createSensor(@NonNull IBinder deviceToken, @NonNull VirtualSensorConfig config) {
-        Objects.requireNonNull(deviceToken);
+    int createSensor(@NonNull IBinder sensorToken, @NonNull VirtualSensorConfig config) {
+        Objects.requireNonNull(sensorToken);
         Objects.requireNonNull(config);
         try {
-            createSensorInternal(deviceToken, config);
+            return createSensorInternal(sensorToken, config);
         } catch (SensorCreationException e) {
             throw new RuntimeException(
                     "Failed to create virtual sensor '" + config.getName() + "'.", e);
         }
     }
 
-    private void createSensorInternal(IBinder deviceToken, VirtualSensorConfig config)
+    private int createSensorInternal(IBinder sensorToken, VirtualSensorConfig config)
             throws SensorCreationException {
-        final SensorManagerInternal.RuntimeSensorStateChangeCallback runtimeSensorCallback =
-                (enabled, samplingPeriodMicros, batchReportLatencyMicros) -> {
-                    IVirtualSensorStateChangeCallback callback = config.getStateChangeCallback();
-                    if (callback != null) {
-                        try {
-                            callback.onStateChanged(
-                                    enabled, samplingPeriodMicros, batchReportLatencyMicros);
-                        } catch (RemoteException e) {
-                            throw new RuntimeException("Failed to call sensor callback.", e);
-                        }
-                    }
-                };
-
         final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId,
                 config.getType(), config.getName(),
                 config.getVendor() == null ? "" : config.getVendor(),
-                runtimeSensorCallback);
+                mRuntimeSensorCallback);
         if (handle <= 0) {
             throw new SensorCreationException("Received an invalid virtual sensor handle.");
         }
@@ -104,8 +106,8 @@
         // The handle is valid from here, so ensure that all failures clean it up.
         final BinderDeathRecipient binderDeathRecipient;
         try {
-            binderDeathRecipient = new BinderDeathRecipient(deviceToken);
-            deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
+            binderDeathRecipient = new BinderDeathRecipient(sensorToken);
+            sensorToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
         } catch (RemoteException e) {
             mSensorManagerInternal.removeRuntimeSensor(handle);
             throw new SensorCreationException("Client died before sensor could be created.", e);
@@ -114,8 +116,9 @@
         synchronized (mLock) {
             SensorDescriptor sensorDescriptor = new SensorDescriptor(
                     handle, config.getType(), config.getName(), binderDeathRecipient);
-            mSensorDescriptors.put(deviceToken, sensorDescriptor);
+            mSensorDescriptors.put(sensorToken, sensorDescriptor);
         }
+        return handle;
     }
 
     boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) {
@@ -178,6 +181,39 @@
         }
     }
 
+    private final class RuntimeSensorCallbackWrapper
+            implements SensorManagerInternal.RuntimeSensorCallback {
+        @Nullable
+        private IVirtualSensorCallback mCallback;
+
+        RuntimeSensorCallbackWrapper(@Nullable IVirtualSensorCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros,
+                int batchReportLatencyMicros) {
+            if (mCallback == null) {
+                Slog.e(TAG, "No sensor callback configured for sensor handle " + handle);
+                return BAD_VALUE;
+            }
+            VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, handle);
+            if (sensor == null) {
+                Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId
+                        + " and sensor handle=" + handle);
+                return BAD_VALUE;
+            }
+            try {
+                mCallback.onConfigurationChanged(sensor, enabled, samplingPeriodMicros,
+                        batchReportLatencyMicros);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to call sensor callback: " + e);
+                return UNKNOWN_ERROR;
+            }
+            return OK;
+        }
+    }
+
     @VisibleForTesting
     static final class SensorDescriptor {
 
@@ -207,10 +243,10 @@
     }
 
     private final class BinderDeathRecipient implements IBinder.DeathRecipient {
-        private final IBinder mDeviceToken;
+        private final IBinder mSensorToken;
 
-        BinderDeathRecipient(IBinder deviceToken) {
-            mDeviceToken = deviceToken;
+        BinderDeathRecipient(IBinder sensorToken) {
+            mSensorToken = sensorToken;
         }
 
         @Override
@@ -219,7 +255,7 @@
             // quitting, which removes this death recipient. If this is invoked, the remote end
             // died, or they disposed of the object without properly unregistering.
             Slog.e(TAG, "Virtual sensor controller binder died");
-            unregisterSensor(mDeviceToken);
+            unregisterSensor(mSensorToken);
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 7fce442..b4dcf43 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -41,6 +41,7 @@
 import android.companion.virtual.VirtualDeviceParams;
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.companion.virtual.sensor.VirtualSensor;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.content.ComponentName;
@@ -86,6 +87,8 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -131,6 +134,11 @@
     @GuardedBy("mVirtualDeviceLock")
     @Nullable
     private LocaleList mLocaleList = null;
+    // This device's sensors, keyed by sensor handle.
+    @GuardedBy("mVirtualDeviceLock")
+    private SparseArray<VirtualSensor> mVirtualSensors = new SparseArray<>();
+    @GuardedBy("mVirtualDeviceLock")
+    private List<VirtualSensor> mVirtualSensorList = null;
 
     private ActivityListener createListenerAdapter() {
         return new ActivityListener() {
@@ -240,10 +248,15 @@
             mInputController = inputController;
         }
         if (sensorController == null) {
-            mSensorController = new SensorController(mVirtualDeviceLock, mDeviceId);
+            mSensorController = new SensorController(
+                    mVirtualDeviceLock, mDeviceId, mParams.getVirtualSensorCallback());
         } else {
             mSensorController = sensorController;
         }
+        final List<VirtualSensorConfig> virtualSensorConfigs = mParams.getVirtualSensorConfigs();
+        for (int i = 0; i < virtualSensorConfigs.size(); ++i) {
+            createVirtualSensor(virtualSensorConfigs.get(i));
+        }
         mCameraAccessController = cameraAccessController;
         mCameraAccessController.startObservingIfNeeded();
         mOnDeviceCloseListener = onDeviceCloseListener;
@@ -384,6 +397,8 @@
                 mVirtualAudioController = null;
             }
             mLocaleList = null;
+            mVirtualSensorList = null;
+            mVirtualSensors.clear();
         }
         mOnDeviceCloseListener.onClose(mDeviceId);
         mAppToken.unlinkToDeath(this, 0);
@@ -698,17 +713,17 @@
         }
     }
 
-    @Override // Binder call
-    @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public void createVirtualSensor(
-            @NonNull IBinder deviceToken,
-            @NonNull VirtualSensorConfig config) {
-        super.createVirtualSensor_enforcePermission();
-        Objects.requireNonNull(config);
-        Objects.requireNonNull(deviceToken);
+    private void createVirtualSensor(@NonNull VirtualSensorConfig config) {
+        final IBinder sensorToken =
+                new Binder("android.hardware.sensor.VirtualSensor:" + config.getName());
         final long ident = Binder.clearCallingIdentity();
         try {
-            mSensorController.createSensor(deviceToken, config);
+            int handle = mSensorController.createSensor(sensorToken, config);
+            VirtualSensor sensor = new VirtualSensor(handle, config.getType(), config.getName(),
+                    this, sensorToken);
+            synchronized (mVirtualDeviceLock) {
+                mVirtualSensors.put(handle, sensor);
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -716,13 +731,24 @@
 
     @Override // Binder call
     @EnforcePermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
-    public void unregisterSensor(@NonNull IBinder token) {
-        super.unregisterSensor_enforcePermission();
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mSensorController.unregisterSensor(token);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
+    @Nullable
+    public List<VirtualSensor> getVirtualSensorList() {
+        super.getVirtualSensorList_enforcePermission();
+        synchronized (mVirtualDeviceLock) {
+            if (mVirtualSensorList == null) {
+                mVirtualSensorList = new ArrayList<>();
+                for (int i = 0; i < mVirtualSensors.size(); ++i) {
+                    mVirtualSensorList.add(mVirtualSensors.valueAt(i));
+                }
+                mVirtualSensorList = Collections.unmodifiableList(mVirtualSensorList);
+            }
+            return mVirtualSensorList;
+        }
+    }
+
+    VirtualSensor getVirtualSensorByHandle(int handle) {
+        synchronized (mVirtualDeviceLock) {
+            return mVirtualSensors.get(handle);
         }
     }
 
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index f39c32df..9bb05a6 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -34,6 +34,7 @@
 import android.companion.virtual.VirtualDevice;
 import android.companion.virtual.VirtualDeviceManager;
 import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.sensor.VirtualSensor;
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.display.DisplayManagerInternal;
@@ -470,6 +471,17 @@
         }
 
         @Override
+        public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) {
+            synchronized (mVirtualDeviceManagerLock) {
+                VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
+                if (virtualDevice != null) {
+                    return virtualDevice.getVirtualSensorByHandle(handle);
+                }
+            }
+            return null;
+        }
+
+        @Override
         public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) {
             ArraySet<Integer> result = new ArraySet<>();
             synchronized (mVirtualDeviceManagerLock) {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 6fb5730..26d0860 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -1329,6 +1329,11 @@
 
     /** @deprecated For legacy shell command only. */
     @Deprecated
+    public abstract void legacyForceDexOpt(@NonNull String packageName)
+            throws LegacyDexoptDisabledException;
+
+    /** @deprecated For legacy shell command only. */
+    @Deprecated
     public abstract void legacyReconcileSecondaryDexFiles(String packageName)
             throws LegacyDexoptDisabledException;
 }
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 54c47b2..669dcfc 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -892,9 +892,9 @@
         pw.println("Battery service (battery) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  get [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid]");
-        pw.println(
-                "  set [-f] [ac|usb|wireless|status|level|temp|present|counter|invalid] <value>");
+        pw.println("  get [-f] [ac|usb|wireless|dock|status|level|temp|present|counter|invalid]");
+        pw.println("  set [-f] "
+                + "[ac|usb|wireless|dock|status|level|temp|present|counter|invalid] <value>");
         pw.println("    Force a battery property value, freezing battery state.");
         pw.println("    -f: force a battery change broadcast be sent, prints new sequence.");
         pw.println("  unplug [-f]");
@@ -954,6 +954,9 @@
                     case "wireless":
                         pw.println(mHealthInfo.chargerWirelessOnline);
                         break;
+                    case "dock":
+                        pw.println(mHealthInfo.chargerDockOnline);
+                        break;
                     case "status":
                         pw.println(mHealthInfo.batteryStatus);
                         break;
@@ -1008,6 +1011,9 @@
                         case "wireless":
                             mHealthInfo.chargerWirelessOnline = Integer.parseInt(value) != 0;
                             break;
+                        case "dock":
+                            mHealthInfo.chargerDockOnline = Integer.parseInt(value) != 0;
+                            break;
                         case "status":
                             mHealthInfo.batteryStatus = Integer.parseInt(value);
                             break;
@@ -1085,6 +1091,7 @@
         mHealthInfo.chargerAcOnline = false;
         mHealthInfo.chargerUsbOnline = false;
         mHealthInfo.chargerWirelessOnline = false;
+        mHealthInfo.chargerDockOnline = false;
         mUpdatesStopped = true;
         Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw));
     }
@@ -1127,6 +1134,7 @@
                 pw.println("  AC powered: " + mHealthInfo.chargerAcOnline);
                 pw.println("  USB powered: " + mHealthInfo.chargerUsbOnline);
                 pw.println("  Wireless powered: " + mHealthInfo.chargerWirelessOnline);
+                pw.println("  Dock powered: " + mHealthInfo.chargerDockOnline);
                 pw.println("  Max charging current: " + mHealthInfo.maxChargingCurrentMicroamps);
                 pw.println("  Max charging voltage: " + mHealthInfo.maxChargingVoltageMicrovolts);
                 pw.println("  Charge counter: " + mHealthInfo.batteryChargeCounterUah);
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index eafd3e0..9d07365 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -263,26 +263,26 @@
             Map<Integer, byte[]> contentDigests = computeApkContentDigest(apkPath);
             if (contentDigests == null) {
                 Slog.d(TAG, "Failed to compute content digest for " + apkPath);
-                return new Checksum(0, new byte[] { -1 });
-            }
-
-            // in this iteration, we'll be supporting only 2 types of digests:
-            // CHUNKED_SHA256 and CHUNKED_SHA512.
-            // And only one of them will be available per package.
-            if (contentDigests.containsKey(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256)) {
-                return new Checksum(
-                        Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
-                        contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256));
-            } else if (contentDigests.containsKey(
-                    ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512)) {
-                return new Checksum(
-                        Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
-                        contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512));
             } else {
-                // TODO(b/259423111): considering putting the raw values for the algorithm & digest
-                //  into the bundle to track potential other digest algorithms that may be in use
-                return new Checksum(0, new byte[] { -1 });
+                // in this iteration, we'll be supporting only 2 types of digests:
+                // CHUNKED_SHA256 and CHUNKED_SHA512.
+                // And only one of them will be available per package.
+                if (contentDigests.containsKey(
+                            ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256)) {
+                    return new Checksum(
+                            Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA256,
+                            contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256));
+                } else if (contentDigests.containsKey(
+                        ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512)) {
+                    return new Checksum(
+                            Checksum.TYPE_PARTIAL_MERKLE_ROOT_1M_SHA512,
+                            contentDigests.get(ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512));
+                }
             }
+            // When something went wrong, fall back to simple sha256.
+            byte[] digest = PackageUtils.computeSha256DigestForLargeFileAsBytes(apkPath,
+                    PackageUtils.createLargeFileBuffer());
+            return new Checksum(Checksum.TYPE_WHOLE_SHA256, digest);
         }
 
 
@@ -1199,7 +1199,7 @@
     }
 
     /**
-     * JobService to measure all covered binaries and record result to Westworld.
+     * JobService to measure all covered binaries and record results to statsd.
      */
     public static class UpdateMeasurementsJobService extends JobService {
         private static long sTimeLastRanMs = 0;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1fe097e..b7985a5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -13692,6 +13692,17 @@
             // not be used generally, so we will be marking them as exported by default
             boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
                     DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+
+            // A receiver that is visible to instant apps must also be exported.
+            final boolean unexportedReceiverVisibleToInstantApps =
+                    ((flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0) && (
+                            (flags & Context.RECEIVER_NOT_EXPORTED) != 0);
+            if (unexportedReceiverVisibleToInstantApps && requireExplicitFlagForDynamicReceivers) {
+                throw new IllegalArgumentException(
+                        "Receiver can't specify both RECEIVER_VISIBLE_TO_INSTANT_APPS and "
+                                + "RECEIVER_NOT_EXPORTED flag");
+            }
+
             // STOPSHIP(b/259139792): Allow apps that are currently targeting U and in process of
             // updating their receivers to be exempt from this requirement until their receivers
             // are flagged.
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 14ecf9f..8b34fe0 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -376,7 +376,7 @@
         return DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                 ENABLE_DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER,
-                false); // assume false if the property is unknown
+                true);
     }
 
     /**
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index b001f3d..a88055d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -1556,6 +1556,7 @@
                 case MSG_I_BT_SERVICE_DISCONNECTED_PROFILE:
                     if (msg.arg1 != BluetoothProfile.HEADSET) {
                         synchronized (mDeviceStateLock) {
+                            mBtHelper.onBtProfileDisconnected(msg.arg1);
                             mDeviceInventory.onBtProfileDisconnected(msg.arg1);
                         }
                     } else {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index df65dbd..4ab23c5 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -278,7 +278,11 @@
         }
         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
-        mA2dp.setAvrcpAbsoluteVolume(index);
+        try {
+            mA2dp.setAvrcpAbsoluteVolume(index);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while changing abs volume", e);
+        }
     }
 
     /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
@@ -286,7 +290,12 @@
         if (mA2dp == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
-        final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
+        final BluetoothCodecStatus btCodecStatus = null;
+        try {
+            mA2dp.getCodecStatus(device);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while getting status of " + device, e);
+        }
         if (btCodecStatus == null) {
             return AudioSystem.AUDIO_FORMAT_DEFAULT;
         }
@@ -420,7 +429,11 @@
         }
         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex));
-        mLeAudio.setVolume(volume);
+        try {
+            mLeAudio.setVolume(volume);
+        } catch (Exception e) {
+            Log.e(TAG, "Exception while setting LE volume", e);
+        }
     }
 
     /*package*/ synchronized void setHearingAidVolume(int index, int streamType,
@@ -446,7 +459,11 @@
             AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
                     AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
         }
-        mHearingAid.setVolume(gainDB);
+        try {
+            mHearingAid.setVolume(gainDB);
+        } catch (Exception e) {
+            Log.i(TAG, "Exception while setting hearing aid volume", e);
+        }
     }
 
     /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
@@ -471,7 +488,7 @@
     }
 
     // @GuardedBy("AudioDeviceBroker.mSetModeLock")
-    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void resetBluetoothSco() {
         mScoAudioState = SCO_STATE_INACTIVE;
         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -486,6 +503,35 @@
         mBluetoothHeadset = null;
     }
 
+    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+    /*package*/ synchronized void onBtProfileDisconnected(int profile) {
+        switch (profile) {
+            case BluetoothProfile.A2DP:
+                mA2dp = null;
+                break;
+            case BluetoothProfile.HEARING_AID:
+                mHearingAid = null;
+                break;
+            case BluetoothProfile.LE_AUDIO:
+                mLeAudio = null;
+                break;
+
+            case BluetoothProfile.A2DP_SINK:
+            case BluetoothProfile.LE_AUDIO_BROADCAST:
+                // shouldn't be received here as profile doesn't involve BtHelper
+                Log.e(TAG, "onBtProfileDisconnected: Not a profile handled by BtHelper "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+
+            default:
+                // Not a valid profile to disconnect
+                Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
+                        + BluetoothProfile.getProfileName(profile));
+                break;
+        }
+    }
+
+    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
     /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
         if (profile == BluetoothProfile.HEADSET) {
             onHeadsetProfileConnected((BluetoothHeadset) proxy);
@@ -671,7 +717,6 @@
                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
                     switch(profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
@@ -681,6 +726,10 @@
                             mDeviceBroker.postBtProfileConnected(profile, proxy);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
@@ -689,14 +738,19 @@
 
                     switch (profile) {
                         case BluetoothProfile.A2DP:
-                        case BluetoothProfile.A2DP_SINK:
                         case BluetoothProfile.HEADSET:
                         case BluetoothProfile.HEARING_AID:
                         case BluetoothProfile.LE_AUDIO:
-                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+                                    "BT profile service: disconnecting "
+                                        + BluetoothProfile.getProfileName(profile) + " profile"));
                             mDeviceBroker.postBtProfileDisconnected(profile);
                             break;
 
+                        case BluetoothProfile.A2DP_SINK:
+                            // no A2DP sink functionality handled by BtHelper
+                        case BluetoothProfile.LE_AUDIO_BROADCAST:
+                            // no broadcast functionality handled by BtHelper
                         default:
                             break;
                     }
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index 001cb10..a335338 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.companion.virtual.IVirtualDevice;
+import android.companion.virtual.sensor.VirtualSensor;
 import android.os.LocaleList;
 import android.util.ArraySet;
 
@@ -78,6 +79,15 @@
     public abstract int getDeviceOwnerUid(int deviceId);
 
     /**
+     * Returns the VirtualSensor for the given deviceId and sensor handle, if any.
+     *
+     * @param deviceId the virtual device that owns the sensor
+     * @param handle the sensor handle
+     * @return the VirtualSensor with the given handle, or {@code null} if no such sensor exists.
+     */
+    public abstract @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle);
+
+    /**
      * Finds VirtualDevices where an app is running.
      *
      * @param uid - the app's uid
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 1305d63..f5ca8aa 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -138,6 +138,7 @@
     private static final int MSG_UPDATE_RBC = 11;
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
+    private static final int MSG_SWITCH_USER = 14;
 
     private static final int PROXIMITY_UNKNOWN = -1;
     private static final int PROXIMITY_NEGATIVE = 0;
@@ -714,6 +715,11 @@
 
     @Override
     public void onSwitchUser(@UserIdInt int newUserId) {
+        Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId);
+        mHandler.sendMessage(msg);
+    }
+
+    private void handleOnSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         handleBrightnessModeChange();
         if (mBrightnessTracker != null) {
@@ -3073,6 +3079,10 @@
                 case MSG_STATSD_HBM_BRIGHTNESS:
                     logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
                     break;
+
+                case MSG_SWITCH_USER:
+                    handleOnSwitchUser(msg.arg1);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 82faa12..5667ddf 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -135,6 +135,7 @@
     private static final int MSG_UPDATE_RBC = 9;
     private static final int MSG_BRIGHTNESS_RAMP_DONE = 10;
     private static final int MSG_STATSD_HBM_BRIGHTNESS = 11;
+    private static final int MSG_SWITCH_USER = 12;
 
     private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
 
@@ -605,6 +606,11 @@
 
     @Override
     public void onSwitchUser(@UserIdInt int newUserId) {
+        Message msg = mHandler.obtainMessage(MSG_SWITCH_USER, newUserId);
+        mHandler.sendMessage(msg);
+    }
+
+    private void handleOnSwitchUser(@UserIdInt int newUserId) {
         handleSettingsChange(true /* userSwitch */);
         handleBrightnessModeChange();
         if (mBrightnessTracker != null) {
@@ -2573,6 +2579,10 @@
                 case MSG_STATSD_HBM_BRIGHTNESS:
                     logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
                     break;
+
+                case MSG_SWITCH_USER:
+                    handleOnSwitchUser(msg.arg1);
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c1b3834..fd9f8d1 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -393,12 +393,17 @@
     static final int INVALID_UID = -1;
     static final String ROOT_PKG = "root";
 
-    static final String[] DEFAULT_ALLOWED_ADJUSTMENTS = new String[] {
+    static final String[] ALLOWED_ADJUSTMENTS = new String[] {
+            Adjustment.KEY_PEOPLE,
+            Adjustment.KEY_SNOOZE_CRITERIA,
+            Adjustment.KEY_USER_SENTIMENT,
             Adjustment.KEY_CONTEXTUAL_ACTIONS,
             Adjustment.KEY_TEXT_REPLIES,
-            Adjustment.KEY_NOT_CONVERSATION,
             Adjustment.KEY_IMPORTANCE,
-            Adjustment.KEY_RANKING_SCORE
+            Adjustment.KEY_IMPORTANCE_PROPOSAL,
+            Adjustment.KEY_SENSITIVE_CONTENT,
+            Adjustment.KEY_RANKING_SCORE,
+            Adjustment.KEY_NOT_CONVERSATION
     };
 
     static final String[] NON_BLOCKABLE_DEFAULT_ROLES = new String[] {
@@ -2567,27 +2572,6 @@
             for (String name : properties.getKeyset()) {
                 if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
                     mAssistants.resetDefaultAssistantsIfNecessary();
-                } else if (SystemUiDeviceConfigFlags.ENABLE_NAS_PRIORITIZER.equals(name)) {
-                    String value = properties.getString(name, null);
-                    if ("true".equals(value)) {
-                        mAssistants.allowAdjustmentType(Adjustment.KEY_IMPORTANCE);
-                    } else if ("false".equals(value)) {
-                        mAssistants.disallowAdjustmentType(Adjustment.KEY_IMPORTANCE);
-                    }
-                } else if (SystemUiDeviceConfigFlags.ENABLE_NAS_RANKING.equals(name)) {
-                    String value = properties.getString(name, null);
-                    if ("true".equals(value)) {
-                        mAssistants.allowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
-                    } else if ("false".equals(value)) {
-                        mAssistants.disallowAdjustmentType(Adjustment.KEY_RANKING_SCORE);
-                    }
-                } else if (SystemUiDeviceConfigFlags.ENABLE_NAS_NOT_CONVERSATION.equals(name)) {
-                    String value = properties.getString(name, null);
-                    if ("true".equals(value)) {
-                        mAssistants.allowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
-                    } else if ("false".equals(value)) {
-                        mAssistants.disallowAdjustmentType(Adjustment.KEY_NOT_CONVERSATION);
-                    }
                 } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
                     String value = properties.getString(name, null);
                     if ("true".equals(value)) {
@@ -4256,22 +4240,6 @@
             return mAssistants.getAllowedAssistantAdjustments();
         }
 
-        @Override
-        public void allowAssistantAdjustment(String adjustmentType) {
-            checkCallerIsSystemOrSystemUiOrShell();
-            mAssistants.allowAdjustmentType(adjustmentType);
-
-            handleSavePolicyFile();
-        }
-
-        @Override
-        public void disallowAssistantAdjustment(String adjustmentType) {
-            checkCallerIsSystemOrSystemUiOrShell();
-            mAssistants.disallowAdjustmentType(adjustmentType);
-
-            handleSavePolicyFile();
-        }
-
         /**
          * @deprecated Use {@link #getActiveNotificationsWithAttribution(String, String)} instead.
          */
@@ -10146,8 +10114,6 @@
     public class NotificationAssistants extends ManagedServices {
         static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";
 
-        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES_OLD = "q_allowed_adjustments";
-        private static final String TAG_ALLOWED_ADJUSTMENT_TYPES = "s_allowed_adjustments";
         private static final String ATT_TYPES = "types";
 
         private final Object mLock = new Object();
@@ -10224,10 +10190,9 @@
                 IPackageManager pm) {
             super(context, lock, up, pm);
 
-            // Add all default allowed adjustment types. Will be overwritten by values in xml,
-            // if they exist
-            for (int i = 0; i < DEFAULT_ALLOWED_ADJUSTMENTS.length; i++) {
-                mAllowedAdjustments.add(DEFAULT_ALLOWED_ADJUSTMENTS[i]);
+            // Add all default allowed adjustment types.
+            for (int i = 0; i < ALLOWED_ADJUSTMENTS.length; i++) {
+                mAllowedAdjustments.add(ALLOWED_ADJUSTMENTS[i]);
             }
         }
 
@@ -10285,52 +10250,6 @@
             return android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE;
         }
 
-        @Override
-        protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
-            synchronized (mLock) {
-                out.startTag(null, TAG_ALLOWED_ADJUSTMENT_TYPES);
-                out.attribute(null, ATT_TYPES, TextUtils.join(",", mAllowedAdjustments));
-                out.endTag(null, TAG_ALLOWED_ADJUSTMENT_TYPES);
-            }
-        }
-
-        @Override
-        protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
-            if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)
-                    || TAG_ALLOWED_ADJUSTMENT_TYPES.equals(tag)) {
-                final String types = XmlUtils.readStringAttribute(parser, ATT_TYPES);
-                synchronized (mLock) {
-                    mAllowedAdjustments.clear();
-                    if (!TextUtils.isEmpty(types)) {
-                        mAllowedAdjustments.addAll(Arrays.asList(types.split(",")));
-                    }
-                    if (TAG_ALLOWED_ADJUSTMENT_TYPES_OLD.equals(tag)) {
-                        if (DEBUG) Slog.d(TAG, "Migrate allowed adjustments.");
-                        mAllowedAdjustments.addAll(
-                                Arrays.asList(DEFAULT_ALLOWED_ADJUSTMENTS));
-                    }
-                }
-            }
-        }
-
-        protected void allowAdjustmentType(String type) {
-            synchronized (mLock) {
-                mAllowedAdjustments.add(type);
-            }
-            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
-                mHandler.post(() -> notifyCapabilitiesChanged(info));
-            }
-        }
-
-        protected void disallowAdjustmentType(String type) {
-            synchronized (mLock) {
-                mAllowedAdjustments.remove(type);
-            }
-            for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
-                    mHandler.post(() -> notifyCapabilitiesChanged(info));
-            }
-        }
-
         protected List<String> getAllowedAssistantAdjustments() {
             synchronized (mLock) {
                 List<String> types = new ArrayList<>();
@@ -10402,15 +10321,6 @@
             mIsUserChanged.put(userId, set);
         }
 
-        private void notifyCapabilitiesChanged(final ManagedServiceInfo info) {
-            final INotificationListener assistant = (INotificationListener) info.service;
-            try {
-                assistant.onAllowedAdjustmentsChanged();
-            } catch (RemoteException ex) {
-                Slog.e(TAG, "unable to notify assistant (capabilities): " + info, ex);
-            }
-        }
-
         private void notifySeen(final ManagedServiceInfo info,
                 final ArrayList<String> keys) {
             final INotificationListener assistant = (INotificationListener) info.service;
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index d8bfa59..1bd5b99 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -566,7 +566,10 @@
                 mPm.getDexManager().getPackageUseInfoOrDefault(p.getPackageName()), options);
     }
 
-    public void forceDexOpt(@NonNull Computer snapshot, String packageName) {
+    /** @deprecated For legacy shell command only. */
+    @Deprecated
+    public void forceDexOpt(@NonNull Computer snapshot, String packageName)
+            throws LegacyDexoptDisabledException {
         PackageManagerServiceUtils.enforceSystemOrRoot("forceDexOpt");
 
         final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
@@ -586,19 +589,7 @@
                 getDefaultCompilerFilter(), null /* splitName */,
                 DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE);
 
-        @DexOptResult int res;
-        if (useArtService()) {
-            // performDexOptWithArtService ignores the snapshot and takes its own, so it can race
-            // with the package checks above, but at worst the effect is only a bit less friendly
-            // error below.
-            res = performDexOptWithArtService(options, ArtFlags.FLAG_SHOULD_INCLUDE_DEPENDENCIES);
-        } else {
-            try {
-                res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
-            } catch (LegacyDexoptDisabledException e) {
-                throw new RuntimeException(e);
-            }
-        }
+        @DexOptResult int res = performDexOptInternalWithDependenciesLI(pkg, packageState, options);
 
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
diff --git a/services/core/java/com/android/server/pm/IPackageManagerBase.java b/services/core/java/com/android/server/pm/IPackageManagerBase.java
index d4e3549..d39cac0 100644
--- a/services/core/java/com/android/server/pm/IPackageManagerBase.java
+++ b/services/core/java/com/android/server/pm/IPackageManagerBase.java
@@ -303,12 +303,6 @@
 
     @Override
     @Deprecated
-    public final void forceDexOpt(String packageName) {
-        mDexOptHelper.forceDexOpt(snapshot(), packageName);
-    }
-
-    @Override
-    @Deprecated
     public final ActivityInfo getActivityInfo(ComponentName component,
             @PackageManager.ComponentInfoFlagsBits long flags, int userId) {
         return snapshot().getActivityInfo(component, flags, userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 382be45..1c24cec 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6679,6 +6679,13 @@
         /** @deprecated For legacy shell command only. */
         @Override
         @Deprecated
+        public void legacyForceDexOpt(String packageName) throws LegacyDexoptDisabledException {
+            mDexOptHelper.forceDexOpt(snapshotComputer(), packageName);
+        }
+
+        /** @deprecated For legacy shell command only. */
+        @Override
+        @Deprecated
         public void legacyReconcileSecondaryDexFiles(String packageName)
                 throws LegacyDexoptDisabledException {
             final Computer snapshot = snapshotComputer();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d322fa2..74bd0092a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2002,8 +2002,8 @@
         return 0;
     }
 
-    public int runForceDexOpt() throws RemoteException {
-        mInterface.forceDexOpt(getNextArgRequired());
+    public int runForceDexOpt() throws RemoteException, LegacyDexoptDisabledException {
+        mPm.legacyForceDexOpt(getNextArgRequired());
         return 0;
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 3cbaebe..36efc0d 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -238,8 +238,9 @@
      * the user is created (as it will be passed back to it through
      * {@link UserLifecycleListener#onUserCreated(UserInfo, Object)});
      */
-    public abstract UserInfo createUserEvenWhenDisallowed(String name, String userType,
-            int flags, String[] disallowedPackages, @Nullable Object token)
+    public abstract @NonNull UserInfo createUserEvenWhenDisallowed(
+            @Nullable String name, @NonNull String userType, @UserInfo.UserInfoFlag int flags,
+            @Nullable String[] disallowedPackages, @Nullable Object token)
             throws UserManager.CheckedUserOperationException;
 
     /**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 372b580..a22ea96 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4495,9 +4495,11 @@
      * as well as for {@link UserManager#USER_TYPE_FULL_RESTRICTED}.
      */
     @Override
-    public UserInfo createProfileForUserWithThrow(@Nullable String name, @NonNull String userType,
-            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
+    public @NonNull UserInfo createProfileForUserWithThrow(
+            @Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
+            @UserIdInt int userId, @Nullable String[] disallowedPackages)
             throws ServiceSpecificException {
+
         checkCreateUsersPermission(flags);
         try {
             return createUserInternal(name, userType, flags, userId, disallowedPackages);
@@ -4510,10 +4512,11 @@
      * @see #createProfileForUser
      */
     @Override
-    public UserInfo createProfileForUserEvenWhenDisallowedWithThrow(String name,
-            @NonNull String userType,
-            @UserInfoFlag int flags, @UserIdInt int userId, @Nullable String[] disallowedPackages)
+    public @NonNull UserInfo createProfileForUserEvenWhenDisallowedWithThrow(
+            @Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
+            @UserIdInt int userId, @Nullable String[] disallowedPackages)
             throws ServiceSpecificException {
+
         checkCreateUsersPermission(flags);
         try {
             return createUserInternalUnchecked(name, userType, flags, userId,
@@ -4524,9 +4527,10 @@
     }
 
     @Override
-    public UserInfo createUserWithThrow(String name, @NonNull String userType,
-            @UserInfoFlag int flags)
+    public @NonNull UserInfo createUserWithThrow(
+            @Nullable String name, @NonNull String userType, @UserInfoFlag int flags)
             throws ServiceSpecificException {
+
         checkCreateUsersPermission(flags);
         try {
             return createUserInternal(name, userType, flags, UserHandle.USER_NULL,
@@ -4537,7 +4541,10 @@
     }
 
     @Override
-    public UserInfo preCreateUserWithThrow(String userType) throws ServiceSpecificException {
+    public @NonNull UserInfo preCreateUserWithThrow(
+            @NonNull String userType)
+            throws ServiceSpecificException {
+
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         final int flags = userTypeDetails != null ? userTypeDetails.getDefaultUserInfoFlags() : 0;
 
@@ -4557,10 +4564,12 @@
     }
 
     @Override
-    public UserHandle createUserWithAttributes(
-            String userName, String userType, @UserInfoFlag int flags,
-            Bitmap userIcon,
-            String accountName, String accountType, PersistableBundle accountOptions) {
+    public @NonNull UserHandle createUserWithAttributes(
+            @Nullable String userName, @NonNull String userType, @UserInfoFlag int flags,
+            @Nullable Bitmap userIcon, @Nullable String accountName, @Nullable String accountType,
+            @Nullable PersistableBundle accountOptions)
+            throws ServiceSpecificException {
+
         checkCreateUsersPermission(flags);
 
         if (someUserHasAccountNoChecks(accountName, accountType)) {
@@ -4570,12 +4579,7 @@
 
         UserInfo userInfo;
         try {
-            userInfo = createUserInternal(userName, userType, flags,
-                    UserHandle.USER_NULL, null);
-
-            if (userInfo == null) {
-                throw new ServiceSpecificException(USER_OPERATION_ERROR_UNKNOWN);
-            }
+            userInfo = createUserInternal(userName, userType, flags, UserHandle.USER_NULL, null);
         } catch (UserManager.CheckedUserOperationException e) {
             throw e.toServiceSpecificException();
         }
@@ -4589,7 +4593,8 @@
         return userInfo.getUserHandle();
     }
 
-    private UserInfo createUserInternal(@Nullable String name, @NonNull String userType,
+    private @NonNull UserInfo createUserInternal(
+            @Nullable String name, @NonNull String userType,
             @UserInfoFlag int flags, @UserIdInt int parentId,
             @Nullable String[] disallowedPackages)
             throws UserManager.CheckedUserOperationException {
@@ -4611,11 +4616,12 @@
                 /* preCreate= */ false, disallowedPackages, /* token= */ null);
     }
 
-    private UserInfo createUserInternalUnchecked(@Nullable String name,
-            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
-            boolean preCreate, @Nullable String[] disallowedPackages,
+    private @NonNull UserInfo createUserInternalUnchecked(
+            @Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
+            @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages,
             @Nullable Object token)
             throws UserManager.CheckedUserOperationException {
+
         final int noneUserId = -1;
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("createUser-" + flags);
@@ -4633,27 +4639,31 @@
         }
     }
 
-    private UserInfo createUserInternalUncheckedNoTracing(@Nullable String name,
-            @NonNull String userType, @UserInfoFlag int flags, @UserIdInt int parentId,
-            boolean preCreate, @Nullable String[] disallowedPackages,
+    private @NonNull UserInfo createUserInternalUncheckedNoTracing(
+            @Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
+            @UserIdInt int parentId, boolean preCreate, @Nullable String[] disallowedPackages,
             @NonNull TimingsTraceAndSlog t, @Nullable Object token)
-                    throws UserManager.CheckedUserOperationException {
+            throws UserManager.CheckedUserOperationException {
+
         final UserTypeDetails userTypeDetails = mUserTypes.get(userType);
         if (userTypeDetails == null) {
-            Slog.e(LOG_TAG, "Cannot create user of invalid user type: " + userType);
-            return null;
+            throwCheckedUserOperationException(
+                    "Cannot create user of invalid user type: " + userType,
+                    USER_OPERATION_ERROR_UNKNOWN);
         }
         userType = userType.intern(); // Now that we know it's valid, we can intern it.
         flags |= userTypeDetails.getDefaultUserInfoFlags();
         if (!checkUserTypeConsistency(flags)) {
-            Slog.e(LOG_TAG, "Cannot add user. Flags (" + Integer.toHexString(flags)
-                    + ") and userTypeDetails (" + userType +  ") are inconsistent.");
-            return null;
+            throwCheckedUserOperationException(
+                    "Cannot add user. Flags (" + Integer.toHexString(flags)
+                            + ") and userTypeDetails (" + userType +  ") are inconsistent.",
+                    USER_OPERATION_ERROR_UNKNOWN);
         }
         if ((flags & UserInfo.FLAG_SYSTEM) != 0) {
-            Slog.e(LOG_TAG, "Cannot add user. Flags (" + Integer.toHexString(flags)
-                    + ") indicated SYSTEM user, which cannot be created.");
-            return null;
+            throwCheckedUserOperationException(
+                    "Cannot add user. Flags (" + Integer.toHexString(flags)
+                            + ") indicated SYSTEM user, which cannot be created.",
+                    USER_OPERATION_ERROR_UNKNOWN);
         }
         if (!isUserTypeEnabled(userTypeDetails)) {
             throwCheckedUserOperationException(
@@ -4679,7 +4689,8 @@
         DeviceStorageMonitorInternal dsm = LocalServices
                 .getService(DeviceStorageMonitorInternal.class);
         if (dsm.isMemoryLow()) {
-            throwCheckedUserOperationException("Cannot add user. Not enough space on disk.",
+            throwCheckedUserOperationException(
+                    "Cannot add user. Not enough space on disk.",
                     UserManager.USER_OPERATION_ERROR_LOW_STORAGE);
         }
 
@@ -4707,7 +4718,8 @@
                     }
                 }
                 if (!preCreate && !canAddMoreUsersOfType(userTypeDetails)) {
-                    throwCheckedUserOperationException("Cannot add more users of type " + userType
+                    throwCheckedUserOperationException(
+                            "Cannot add more users of type " + userType
                                     + ". Maximum number of that type already exists.",
                             UserManager.USER_OPERATION_ERROR_MAX_USERS);
                 }
@@ -5332,13 +5344,13 @@
      * @hide
      */
     @Override
-    public UserInfo createRestrictedProfileWithThrow(@Nullable String name, int parentUserId) {
+    public @NonNull UserInfo createRestrictedProfileWithThrow(
+            @Nullable String name, @UserIdInt int parentUserId)
+            throws ServiceSpecificException {
+
         checkCreateUsersPermission("setupRestrictedProfile");
         final UserInfo user = createProfileForUserWithThrow(
                 name, UserManager.USER_TYPE_FULL_RESTRICTED, 0, parentUserId, null);
-        if (user == null) {
-            return null;
-        }
         final long identity = Binder.clearCallingIdentity();
         try {
             setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user.id);
@@ -6339,8 +6351,10 @@
         setSeedAccountDataNoChecks(userId, accountName, accountType, accountOptions, persist);
     }
 
-    private void setSeedAccountDataNoChecks(@UserIdInt int userId, String accountName,
-            String accountType, PersistableBundle accountOptions, boolean persist) {
+    private void setSeedAccountDataNoChecks(@UserIdInt int userId, @Nullable String accountName,
+            @Nullable String accountType, @Nullable PersistableBundle accountOptions,
+            boolean persist) {
+
         synchronized (mPackagesLock) {
             final UserData userData;
             synchronized (mUsersLock) {
@@ -6909,9 +6923,11 @@
         }
 
         @Override
-        public UserInfo createUserEvenWhenDisallowed(String name, @NonNull String userType,
-                @UserInfoFlag int flags, String[] disallowedPackages, @Nullable Object token)
+        public @NonNull UserInfo createUserEvenWhenDisallowed(
+                @Nullable String name, @NonNull String userType, @UserInfoFlag int flags,
+                @Nullable String[] disallowedPackages, @Nullable Object token)
                 throws UserManager.CheckedUserOperationException {
+
             return createUserInternalUnchecked(name, userType, flags,
                     UserHandle.USER_NULL, /* preCreated= */ false, disallowedPackages, token);
         }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 3f2083f..6032fec 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -167,10 +167,10 @@
     );
 
     /**
-     * User restrictions that cannot be set by profile owners of secondary users. When set by DO
-     * they will be applied to all users.
+     * User restrictions that can only be set by profile owners on the main user, or by device
+     * owners. When set by DO they will be applied to all users.
      */
-    private static final Set<String> PRIMARY_USER_ONLY_RESTRICTIONS = Sets.newArraySet(
+    private static final Set<String> MAIN_USER_ONLY_RESTRICTIONS = Sets.newArraySet(
             UserManager.DISALLOW_BLUETOOTH,
             UserManager.DISALLOW_USB_FILE_TRANSFER,
             UserManager.DISALLOW_CONFIG_TETHERING,
@@ -454,14 +454,14 @@
     }
 
     /**
-     * @return true if a restriction is settable by profile owner.  Note it takes a user ID because
-     * some restrictions can be changed by PO only when it's running on the system user.
+     * @return true if a restriction is settable by profile owner.  Note it takes a boolean to say
+     * if the relevant user is the {@link UserManager#isMainUser() MainUser}, because some
+     * restrictions can be changed by PO only when it's running on the main user.
      */
-    public static boolean canProfileOwnerChange(String restriction, int userId) {
+    public static boolean canProfileOwnerChange(String restriction, boolean isMainUser) {
         return !IMMUTABLE_BY_OWNERS.contains(restriction)
                 && !DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction)
-                && !(userId != UserHandle.USER_SYSTEM
-                    && PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction));
+                && !(!isMainUser && MAIN_USER_ONLY_RESTRICTIONS.contains(restriction));
     }
 
     /**
@@ -494,7 +494,7 @@
     public static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType,
             String key) {
         return ((restrictionOwnerType == UserManagerInternal.OWNER_TYPE_DEVICE_OWNER) && (
-                PRIMARY_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
+                MAIN_USER_ONLY_RESTRICTIONS.contains(key) || GLOBAL_RESTRICTIONS.contains(key)))
                 || ((restrictionOwnerType
                 == UserManagerInternal.OWNER_TYPE_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE)
                 && PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS.contains(key))
diff --git a/services/core/java/com/android/server/power/LowPowerStandbyController.java b/services/core/java/com/android/server/power/LowPowerStandbyController.java
index 223bd55..0dc5f76 100644
--- a/services/core/java/com/android/server/power/LowPowerStandbyController.java
+++ b/services/core/java/com/android/server/power/LowPowerStandbyController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
 import static android.os.PowerManager.lowPowerStandbyAllowedReasonsToString;
 
 import android.Manifest;
@@ -59,6 +60,7 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 import com.android.server.LocalServices;
+import com.android.server.PowerAllowlistInternal;
 import com.android.server.net.NetworkPolicyManagerInternal;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -134,7 +136,7 @@
     @GuardedBy("mLock")
     private boolean mEnableCustomPolicy;
 
-    private final BroadcastReceiver mIdleBroadcastReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             switch (intent.getAction()) {
@@ -150,6 +152,8 @@
             }
         }
     };
+    private final TempAllowlistChangeListener mTempAllowlistChangeListener =
+            new TempAllowlistChangeListener();
 
     private final BroadcastReceiver mPackageBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -318,7 +322,7 @@
             updateSettingsLocked();
 
             if (mIsEnabled) {
-                registerBroadcastReceiver();
+                registerListeners();
             }
         }
 
@@ -594,7 +598,7 @@
             onNonInteractive();
         }
 
-        registerBroadcastReceiver();
+        registerListeners();
     }
 
     @GuardedBy("mLock")
@@ -604,7 +608,7 @@
         }
 
         cancelStandbyTimeoutAlarmLocked();
-        unregisterBroadcastReceiver();
+        unregisterListeners();
         updateActiveLocked();
     }
 
@@ -629,13 +633,13 @@
         }
     }
 
-    private void registerBroadcastReceiver() {
+    private void registerListeners() {
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
 
-        mContext.registerReceiver(mIdleBroadcastReceiver, intentFilter);
+        mContext.registerReceiver(mBroadcastReceiver, intentFilter);
 
         IntentFilter packageFilter = new IntentFilter();
         packageFilter.addDataScheme(IntentFilter.SCHEME_PACKAGE);
@@ -648,12 +652,18 @@
         userFilter.addAction(Intent.ACTION_USER_ADDED);
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
+
+        PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
+        pai.registerTempAllowlistChangeListener(mTempAllowlistChangeListener);
     }
 
-    private void unregisterBroadcastReceiver() {
-        mContext.unregisterReceiver(mIdleBroadcastReceiver);
+    private void unregisterListeners() {
+        mContext.unregisterReceiver(mBroadcastReceiver);
         mContext.unregisterReceiver(mPackageBroadcastReceiver);
         mContext.unregisterReceiver(mUserReceiver);
+
+        PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
+        pai.unregisterTempAllowlistChangeListener(mTempAllowlistChangeListener);
     }
 
     @GuardedBy("mLock")
@@ -1197,4 +1207,18 @@
             onSettingsChanged();
         }
     }
+
+    final class TempAllowlistChangeListener implements
+            PowerAllowlistInternal.TempAllowlistChangeListener {
+        @Override
+        public void onAppAdded(int uid) {
+            addToAllowlistInternal(uid, LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST);
+        }
+
+        @Override
+        public void onAppRemoved(int uid) {
+            removeFromAllowlistInternal(uid,
+                    LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
index f17e5e7..41c2fbf 100644
--- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java
+++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java
@@ -58,7 +58,7 @@
      * @return The sensor handle.
      */
     public abstract int createRuntimeSensor(int deviceId, int type, @NonNull String name,
-            @NonNull String vendor, @NonNull RuntimeSensorStateChangeCallback callback);
+            @NonNull String vendor, @NonNull RuntimeSensorCallback callback);
 
     /**
      * Unregisters the sensor with the given handle from the framework.
@@ -95,11 +95,12 @@
      * {@link #createRuntimeSensor}, i.e. the dynamic sensors created via the dynamic sensor HAL are
      * not covered.
      */
-    public interface RuntimeSensorStateChangeCallback {
+    public interface RuntimeSensorCallback {
         /**
          * Invoked when the listeners of the runtime sensor have changed.
+         * Returns an error code if the invocation was unsuccessful, zero otherwise.
          */
-        void onStateChanged(boolean enabled, int samplingPeriodMicros,
+        int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros,
                 int batchReportLatencyMicros);
     }
 }
diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java
index d8e3bdd..9790659 100644
--- a/services/core/java/com/android/server/sensors/SensorService.java
+++ b/services/core/java/com/android/server/sensors/SensorService.java
@@ -56,8 +56,7 @@
     private static native void unregisterProximityActiveListenerNative(long ptr);
 
     private static native int registerRuntimeSensorNative(long ptr, int deviceId, int type,
-            String name, String vendor,
-            SensorManagerInternal.RuntimeSensorStateChangeCallback callback);
+            String name, String vendor, SensorManagerInternal.RuntimeSensorCallback callback);
     private static native void unregisterRuntimeSensorNative(long ptr, int handle);
     private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type,
             long timestampNanos, float[] values);
@@ -96,7 +95,7 @@
     class LocalService extends SensorManagerInternal {
         @Override
         public int createRuntimeSensor(int deviceId, int type, @NonNull String name,
-                @NonNull String vendor, @NonNull RuntimeSensorStateChangeCallback callback) {
+                @NonNull String vendor, @NonNull RuntimeSensorCallback callback) {
             synchronized (mLock) {
                 int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor,
                         callback);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 3c2832e..7aeebbc 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1989,7 +1989,7 @@
      * Called when an app has started replacing its main window.
      */
     void addRelaunchingApp(ActivityRecord app) {
-        if (mSystemBarColorApps.contains(app)) {
+        if (mSystemBarColorApps.contains(app) && !app.hasStartingWindow()) {
             mRelaunchingSystemBarColorApps.add(app);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 69e84c7..ea0e3f3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -6012,7 +6012,7 @@
             Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms");
             mOrientationChangeRedrawRequestTime = 0;
         } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0
-                && mActivityRecord.findMainWindow() == this) {
+                && mActivityRecord.findMainWindow(false /* includeStartingApp */) == this) {
             final long duration =
                     SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime;
             Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms");
diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp
index 10d8b42..356e9a9 100644
--- a/services/core/jni/com_android_server_sensor_SensorService.cpp
+++ b/services/core/jni/com_android_server_sensor_SensorService.cpp
@@ -32,13 +32,13 @@
     "com/android/server/sensors/SensorManagerInternal$ProximityActiveListener"
 
 #define RUNTIME_SENSOR_CALLBACK_CLASS \
-    "com/android/server/sensors/SensorManagerInternal$RuntimeSensorStateChangeCallback"
+    "com/android/server/sensors/SensorManagerInternal$RuntimeSensorCallback"
 
 namespace android {
 
 static JavaVM* sJvm = nullptr;
 static jmethodID sMethodIdOnProximityActive;
-static jmethodID sMethodIdOnStateChanged;
+static jmethodID sMethodIdOnConfigurationChanged;
 
 class NativeSensorService {
 public:
@@ -67,13 +67,13 @@
     };
     sp<ProximityActiveListenerDelegate> mProximityActiveListenerDelegate;
 
-    class RuntimeSensorCallbackDelegate : public SensorService::RuntimeSensorStateChangeCallback {
+    class RuntimeSensorCallbackDelegate : public SensorService::RuntimeSensorCallback {
     public:
         RuntimeSensorCallbackDelegate(JNIEnv* env, jobject callback);
         ~RuntimeSensorCallbackDelegate();
 
-        void onStateChanged(bool enabled, int64_t samplingPeriodNs,
-                            int64_t batchReportLatencyNs) override;
+        status_t onConfigurationChanged(int32_t handle, bool enabled, int64_t samplingPeriodNs,
+                                        int64_t batchReportLatencyNs) override;
 
     private:
         jobject mCallback;
@@ -231,12 +231,13 @@
     AndroidRuntime::getJNIEnv()->DeleteGlobalRef(mCallback);
 }
 
-void NativeSensorService::RuntimeSensorCallbackDelegate::onStateChanged(
-        bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) {
+status_t NativeSensorService::RuntimeSensorCallbackDelegate::onConfigurationChanged(
+        int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) {
     auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
-    jniEnv->CallVoidMethod(mCallback, sMethodIdOnStateChanged, static_cast<jboolean>(enabled),
-                           static_cast<jint>(ns2us(samplingPeriodNs)),
-                           static_cast<jint>(ns2us(batchReportLatencyNs)));
+    return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged,
+                                 static_cast<jint>(handle), static_cast<jboolean>(enabled),
+                                 static_cast<jint>(ns2us(samplingPeriodNs)),
+                                 static_cast<jint>(ns2us(batchReportLatencyNs)));
 }
 
 static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) {
@@ -292,8 +293,8 @@
     jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS);
     sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V");
     jclass runtimeSensorCallbackClass = FindClassOrDie(env, RUNTIME_SENSOR_CALLBACK_CLASS);
-    sMethodIdOnStateChanged =
-            GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onStateChanged", "(ZII)V");
+    sMethodIdOnConfigurationChanged =
+            GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onConfigurationChanged", "(IZII)I");
     return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods,
                                     NELEM(methods));
 }
diff --git a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
index 447c67f..d262fb7 100644
--- a/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/ClearRequestSession.java
@@ -83,7 +83,10 @@
     }
 
     @Override
-    public void onFinalResponseReceived(ComponentName componentName, Void response) {
+    public void onFinalResponseReceived(
+            ComponentName componentName,
+            Void response) {
+        setChosenMetric(componentName);
         respondToClientWithResponseAndFinish();
     }
 
@@ -114,6 +117,8 @@
         Log.i(TAG, "respondToClientWithResponseAndFinish");
         if (isSessionCancelled()) {
             // TODO: Differentiate btw cancelled and false
+            mChosenProviderMetric.setChosenProviderStatus(
+                    MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
             logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
             finishSession(/*propagateCancellation=*/true);
             return;
@@ -122,6 +127,8 @@
             mClientCallback.onSuccess();
             logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ true);
         } catch (RemoteException e) {
+            mChosenProviderMetric.setChosenProviderStatus(
+                    MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE);
             Log.i(TAG, "Issue while propagating the response to the client");
             logApiCalled(RequestType.CLEAR_CREDENTIALS, /* isSuccessful */ false);
         }
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index c8518c5..01e7fb9 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -16,6 +16,9 @@
 
 package com.android.server.credentials;
 
+import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE;
+import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -94,9 +97,14 @@
     public void onFinalResponseReceived(ComponentName componentName,
             @Nullable CreateCredentialResponse response) {
         Log.i(TAG, "onFinalCredentialReceived from: " + componentName.flattenToString());
+        setChosenMetric(componentName);
         if (response != null) {
+            mChosenProviderMetric.setChosenProviderStatus(
+                    METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
             respondToClientWithResponseAndFinish(response);
         } else {
+            mChosenProviderMetric.setChosenProviderStatus(
+                    METRICS_PROVIDER_STATUS_FINAL_FAILURE);
             respondToClientWithErrorAndFinish(CreateCredentialException.TYPE_NO_CREATE_OPTIONS,
                     "Invalid response");
         }
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 34101fe..0334051 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -357,7 +357,6 @@
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
             enforceCallingPackage(callingPackage, callingUid);
-
             // New request session, scoped for this request only.
             final GetRequestSession session =
                     new GetRequestSession(
@@ -497,6 +496,7 @@
             Log.i(TAG, "starting executeCreateCredential with callingPackage: "
                     + callingPackage);
             ICancellationSignal cancelTransport = CancellationSignal.createTransport();
+
             final int userId = UserHandle.getCallingUserId();
             final int callingUid = Binder.getCallingUid();
             enforceCallingPackage(callingPackage, callingUid);
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 3324999..14b45b2 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -16,6 +16,9 @@
 
 package com.android.server.credentials;
 
+import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_FAILURE;
+import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_FINAL_SUCCESS;
+
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
@@ -87,9 +90,14 @@
     public void onFinalResponseReceived(ComponentName componentName,
             @Nullable GetCredentialResponse response) {
         Log.i(TAG, "onFinalCredentialReceived from: " + componentName.flattenToString());
+        setChosenMetric(componentName);
         if (response != null) {
+            mChosenProviderMetric.setChosenProviderStatus(
+                    METRICS_PROVIDER_STATUS_FINAL_SUCCESS);
             respondToClientWithResponseAndFinish(response);
         } else {
+            mChosenProviderMetric.setChosenProviderStatus(
+                    METRICS_PROVIDER_STATUS_FINAL_FAILURE);
             respondToClientWithErrorAndFinish(GetCredentialException.TYPE_NO_CREDENTIAL,
                     "Invalid response from provider");
         }
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
new file mode 100644
index 0000000..27d9836d
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2023 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.credentials;
+
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+/**
+ * For all future metric additions, this will contain their names for local usage after importing
+ * from {@link com.android.internal.util.FrameworkStatsLog}.
+ */
+public class MetricUtilities {
+
+    private static final String TAG = "MetricUtilities";
+
+    // Metrics constants
+    protected static final int METRICS_API_NAME_UNKNOWN =
+            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
+    protected static final int METRICS_API_NAME_GET_CREDENTIAL =
+            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
+    protected static final int METRICS_API_NAME_CREATE_CREDENTIAL =
+            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
+    protected static final int METRICS_API_NAME_CLEAR_CREDENTIAL =
+            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
+    // TODO add isEnabled
+    protected static final int METRICS_API_STATUS_SUCCESS =
+            CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
+    protected static final int METRICS_API_STATUS_FAILURE =
+            CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
+    protected static final int METRICS_API_STATUS_CLIENT_CANCEL =
+            CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_CLIENT_CANCELED;
+    protected static final int METRICS_API_STATUS_USER_CANCEL =
+            CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_USER_CANCELED;
+    protected static final int METRICS_PROVIDER_STATUS_FINAL_FAILURE =
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_FAILURE;
+    protected static final int METRICS_PROVIDER_STATUS_QUERY_FAILURE =
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_FAILURE;
+    protected static final int METRICS_PROVIDER_STATUS_FINAL_SUCCESS =
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_FINAL_SUCCESS;
+    protected static final int METRICS_PROVIDER_STATUS_QUERY_SUCCESS =
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_QUERY_SUCCESS;
+    protected static final int METRICS_PROVIDER_STATUS_UNKNOWN =
+            CREDENTIAL_MANAGER_API_CALLED__CANDIDATE_PROVIDER_STATUS__PROVIDER_UNKNOWN;
+
+
+    /**
+     * This retrieves the uid of any package name, given a context and a component name for the
+     * package. By default, if the desired package uid cannot be found, it will fall back to a
+     * bogus uid.
+     * @return the uid of a given package
+     */
+    protected static int getPackageUid(Context context, ComponentName componentName) {
+        int sessUid = -1;
+        try {
+            // Only for T and above, which is fine for our use case
+            sessUid = context.getPackageManager().getApplicationInfo(
+                    componentName.getPackageName(),
+                    PackageManager.ApplicationInfoFlags.of(0)).uid;
+        } catch (Throwable t) {
+            Log.i(TAG, "Couldn't find required uid");
+        }
+        return sessUid;
+    }
+
+}
diff --git a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
index b20f0cd..ce9fca7 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderClearSession.java
@@ -33,7 +33,7 @@
  *
  * @hide
  */
-public final class ProviderClearSession extends ProviderSession<ClearCredentialStateRequest,
+public final class  ProviderClearSession extends ProviderSession<ClearCredentialStateRequest,
         Void>
         implements
         RemoteCredentialService.ProviderCallbacks<Void> {
@@ -120,6 +120,7 @@
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
             mRemoteCredentialService.onClearCredentialState(mProviderRequest, this);
+            mCandidateProviderMetric.setStartTimeNanoseconds(System.nanoTime());
         }
     }
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
index b4c4233..3245c91 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderCreateSession.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.credentials.CreateCredentialException;
@@ -56,14 +57,11 @@
     // Key to be used as an entry key for a remote entry
     private static final String REMOTE_ENTRY_KEY = "remote_entry_key";
 
-    @NonNull
-    private final Map<String, CreateEntry> mUiSaveEntries = new HashMap<>();
-    /** The complete request to be used in the second round. */
     private final CreateCredentialRequest mCompleteRequest;
 
     private CreateCredentialException mProviderException;
 
-    @Nullable protected Pair<String, CreateEntry> mUiRemoteEntry;
+    private final ProviderResponseDataHandler mProviderResponseDataHandler;
 
     /** Creates a new provider session to be used by the request session. */
     @Nullable public static ProviderCreateSession createNewSession(
@@ -88,7 +86,8 @@
                             createRequestSession.mClientAppInfo,
                             createRequestSession
                                     .mClientRequest.alwaysSendAppInfoToProvider()),
-                    providerCreateRequest
+                    providerCreateRequest,
+                    createRequestSession.mHybridService
             );
         }
         Log.i(TAG, "Unable to create provider session");
@@ -131,23 +130,20 @@
             @UserIdInt int userId,
             @NonNull RemoteCredentialService remoteCredentialService,
             @NonNull BeginCreateCredentialRequest beginCreateRequest,
-            @NonNull CreateCredentialRequest completeCreateRequest) {
+            @NonNull CreateCredentialRequest completeCreateRequest,
+            String hybridService) {
         super(context, info, beginCreateRequest, callbacks, userId,
                 remoteCredentialService);
         mCompleteRequest = completeCreateRequest;
         setStatus(Status.PENDING);
-    }
-
-    /** Returns the save entry maintained in state by this provider session. */
-    public CreateEntry getUiSaveEntry(String entryId) {
-        return mUiSaveEntries.get(entryId);
+        mProviderResponseDataHandler = new ProviderResponseDataHandler(hybridService);
     }
 
     @Override
     public void onProviderResponseSuccess(
             @Nullable BeginCreateCredentialResponse response) {
         Log.i(TAG, "in onProviderResponseSuccess");
-        onUpdateResponse(response);
+        onSetInitialRemoteResponse(response);
     }
 
     /** Called when the provider response resulted in a failure. */
@@ -171,21 +167,18 @@
         }
     }
 
-    private void onUpdateResponse(BeginCreateCredentialResponse response) {
-        Log.i(TAG, "updateResponse with save entries");
+    private void onSetInitialRemoteResponse(BeginCreateCredentialResponse response) {
+        Log.i(TAG, "onSetInitialRemoteResponse with save entries");
         mProviderResponse = response;
-        if (isEmptyResponse(response)) {
+        mProviderResponseDataHandler.addResponseContent(response.getCreateEntries(),
+                response.getRemoteCreateEntry());
+        if (mProviderResponseDataHandler.isEmptyResponse(response)) {
             updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
         } else {
             updateStatusAndInvokeCallback(Status.SAVE_ENTRIES_RECEIVED);
         }
     }
 
-    private boolean isEmptyResponse(BeginCreateCredentialResponse response) {
-        return (response.getCreateEntries() == null || response.getCreateEntries().isEmpty())
-                && response.getRemoteCreateEntry() == null;
-    }
-
     @Override
     @Nullable protected CreateCredentialProviderData prepareUiData()
             throws IllegalArgumentException {
@@ -194,53 +187,37 @@
             Log.i(TAG, "In prepareUiData not in uiInvokingStatus");
             return null;
         }
-        final BeginCreateCredentialResponse response = getProviderResponse();
-        if (response == null) {
-            Log.i(TAG, "In prepareUiData response null");
-            throw new IllegalStateException("Response must be in completion mode");
-        }
-        if (response.getCreateEntries() != null) {
+
+        if (mProviderResponse != null && !mProviderResponseDataHandler.isEmptyResponse()) {
             Log.i(TAG, "In prepareUiData save entries not null");
-            return prepareUiProviderData(
-                    prepareUiSaveEntries(response.getCreateEntries()),
-                    prepareUiRemoteEntry(response.getRemoteCreateEntry()));
+            return mProviderResponseDataHandler.toCreateCredentialProviderData();
         }
         return null;
     }
 
-    private Entry prepareUiRemoteEntry(CreateEntry remoteCreateEntry) {
-        if (remoteCreateEntry == null) {
-            return null;
-        }
-        String entryId = generateUniqueId();
-        Entry remoteEntry = new Entry(REMOTE_ENTRY_KEY, entryId, remoteCreateEntry.getSlice(),
-                setUpFillInIntent());
-        mUiRemoteEntry = new Pair<>(entryId, remoteCreateEntry);
-        return remoteEntry;
-    }
-
     @Override
     public void onUiEntrySelected(String entryType, String entryKey,
             ProviderPendingIntentResponse providerPendingIntentResponse) {
         switch (entryType) {
             case SAVE_ENTRY_KEY:
-                if (mUiSaveEntries.containsKey(entryKey)) {
-                    onSaveEntrySelected(providerPendingIntentResponse);
-                } else {
+                if (mProviderResponseDataHandler.getCreateEntry(entryKey) == null) {
                     Log.i(TAG, "Unexpected save entry key");
                     invokeCallbackOnInternalInvalidState();
+                    return;
                 }
+                onCreateEntrySelected(providerPendingIntentResponse);
                 break;
             case REMOTE_ENTRY_KEY:
-                if (mUiRemoteEntry.first.equals(entryKey)) {
-                    onRemoteEntrySelected(providerPendingIntentResponse);
-                } else {
+                if (mProviderResponseDataHandler.getRemoteEntry(entryKey) == null) {
                     Log.i(TAG, "Unexpected remote entry key");
                     invokeCallbackOnInternalInvalidState();
+                    return;
                 }
+                onRemoteEntrySelected(providerPendingIntentResponse);
                 break;
             default:
                 Log.i(TAG, "Unsupported entry type selected");
+                invokeCallbackOnInternalInvalidState();
         }
     }
 
@@ -248,24 +225,10 @@
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
             mRemoteCredentialService.onCreateCredential(mProviderRequest, this);
+            mCandidateProviderMetric.setStartTimeNanoseconds(System.nanoTime());
         }
     }
 
-    private List<Entry> prepareUiSaveEntries(@NonNull List<CreateEntry> saveEntries) {
-        Log.i(TAG, "in populateUiSaveEntries");
-        List<Entry> uiSaveEntries = new ArrayList<>();
-
-        // Populate the save entries
-        for (CreateEntry createEntry : saveEntries) {
-            String entryId = generateUniqueId();
-            mUiSaveEntries.put(entryId, createEntry);
-            Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
-            uiSaveEntries.add(new Entry(SAVE_ENTRY_KEY, entryId, createEntry.getSlice(),
-                    setUpFillInIntent()));
-        }
-        return uiSaveEntries;
-    }
-
     private Intent setUpFillInIntent() {
         Intent intent = new Intent();
         intent.putExtra(CredentialProviderService.EXTRA_CREATE_CREDENTIAL_REQUEST,
@@ -273,16 +236,7 @@
         return intent;
     }
 
-    private CreateCredentialProviderData prepareUiProviderData(List<Entry> saveEntries,
-            Entry remoteEntry) {
-        return new CreateCredentialProviderData.Builder(
-                mComponentName.flattenToString())
-                .setSaveEntries(saveEntries)
-                .setRemoteEntry(remoteEntry)
-                .build();
-    }
-
-    private void onSaveEntrySelected(ProviderPendingIntentResponse pendingIntentResponse) {
+    private void onCreateEntrySelected(ProviderPendingIntentResponse pendingIntentResponse) {
         CreateCredentialException exception = maybeGetPendingIntentException(
                 pendingIntentResponse);
         if (exception != null) {
@@ -296,7 +250,6 @@
                         pendingIntentResponse.getResultData());
         if (credentialResponse != null) {
             mCallbacks.onFinalResponseReceived(mComponentName, credentialResponse);
-            return;
         } else {
             Log.i(TAG, "onSaveEntrySelected - no response or error found in pending "
                     + "intent response");
@@ -304,6 +257,12 @@
         }
     }
 
+    private void onRemoteEntrySelected(ProviderPendingIntentResponse pendingIntentResponse) {
+        // Response from remote entry should be dealt with similar to a response from a
+        // create entry
+        onCreateEntrySelected(pendingIntentResponse);
+    }
+
     @Nullable
     private CreateCredentialException maybeGetPendingIntentException(
             ProviderPendingIntentResponse pendingIntentResponse) {
@@ -335,4 +294,91 @@
                 CreateCredentialException.TYPE_UNKNOWN,
                 null);
     }
+
+    private class ProviderResponseDataHandler {
+        private final ComponentName mExpectedRemoteEntryProviderService;
+
+        @NonNull
+        private final Map<String, Pair<CreateEntry, Entry>> mUiCreateEntries = new HashMap<>();
+
+        @Nullable private Pair<String, Pair<CreateEntry, Entry>> mUiRemoteEntry = null;
+
+        ProviderResponseDataHandler(String hybridService) {
+            mExpectedRemoteEntryProviderService = ComponentName.unflattenFromString(hybridService);
+        }
+
+        public void addResponseContent(List<CreateEntry> createEntries,
+                CreateEntry remoteEntry) {
+            createEntries.forEach(this::addCreateEntry);
+            setRemoteEntry(remoteEntry);
+        }
+        public void addCreateEntry(CreateEntry createEntry) {
+            String id = generateUniqueId();
+            Entry entry = new Entry(SAVE_ENTRY_KEY,
+                    id, createEntry.getSlice(), setUpFillInIntent());
+            mUiCreateEntries.put(id, new Pair<>(createEntry, entry));
+        }
+
+        public void setRemoteEntry(@Nullable CreateEntry remoteEntry) {
+            if (remoteEntry == null) {
+                mUiRemoteEntry = null;
+                return;
+            }
+            if (!mComponentName.equals(mExpectedRemoteEntryProviderService)) {
+                Log.i(TAG, "Remote entry being dropped as it is not from the service "
+                        + "configured by the OEM.");
+                return;
+            }
+            String id = generateUniqueId();
+            Entry entry = new Entry(REMOTE_ENTRY_KEY,
+                    id, remoteEntry.getSlice(), setUpFillInIntent());
+            mUiRemoteEntry = new Pair<>(id, new Pair<>(remoteEntry, entry));
+        }
+
+        public CreateCredentialProviderData toCreateCredentialProviderData() {
+            return new CreateCredentialProviderData.Builder(
+                    mComponentName.flattenToString())
+                    .setSaveEntries(prepareUiCreateEntries())
+                    .setRemoteEntry(prepareRemoteEntry())
+                    .build();
+        }
+
+        private List<Entry> prepareUiCreateEntries() {
+            List<Entry> createEntries = new ArrayList<>();
+            for (String key : mUiCreateEntries.keySet()) {
+                createEntries.add(mUiCreateEntries.get(key).second);
+            }
+            return createEntries;
+        }
+
+        private Entry prepareRemoteEntry() {
+            if (mUiRemoteEntry == null || mUiRemoteEntry.first == null
+                    || mUiRemoteEntry.second == null) {
+                return null;
+            }
+            return mUiRemoteEntry.second.second;
+        }
+
+        private boolean isEmptyResponse() {
+            return mUiCreateEntries.isEmpty() && mUiRemoteEntry == null;
+        }
+        @Nullable
+        public CreateEntry getRemoteEntry(String entryKey) {
+            return mUiRemoteEntry == null || mUiRemoteEntry
+                    .first == null || !mUiRemoteEntry.first.equals(entryKey)
+                    || mUiRemoteEntry.second == null
+                    ? null : mUiRemoteEntry.second.first;
+        }
+
+        @Nullable
+        public CreateEntry getCreateEntry(String entryKey) {
+            return mUiCreateEntries.get(entryKey) == null
+                    ? null : mUiCreateEntries.get(entryKey).first;
+        }
+
+        public boolean isEmptyResponse(BeginCreateCredentialResponse response) {
+            return (response.getCreateEntries() == null || response.getCreateEntries().isEmpty())
+                    && response.getRemoteCreateEntry() == null;
+        }
+    }
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
index c2a57a8..12074c7 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.credentials.CredentialOption;
@@ -45,7 +46,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.UUID;
+import java.util.Optional;
 
 /**
  * Central provider session that listens for provider callbacks, and maintains provider state.
@@ -69,14 +70,7 @@
 
     @NonNull
     private final Map<String, CredentialOption> mBeginGetOptionToCredentialOptionMap;
-    @NonNull
-    private final Map<String, CredentialEntry> mUiCredentialEntries = new HashMap<>();
-    @NonNull
-    private final Map<String, Action> mUiActionsEntries = new HashMap<>();
-    @Nullable
-    private final Map<String, Action> mUiAuthenticationEntries = new HashMap<>();
 
-    @Nullable protected Pair<String, CredentialEntry> mUiRemoteEntry;
 
     /** The complete request to be used in the second round. */
     private final android.credentials.GetCredentialRequest mCompleteRequest;
@@ -84,6 +78,8 @@
 
     private GetCredentialException mProviderException;
 
+    private final ProviderResponseDataHandler mProviderResponseDataHandler;
+
     /** Creates a new provider session to be used by the request session. */
     @Nullable public static ProviderGetSession createNewSession(
             Context context,
@@ -109,7 +105,8 @@
                             beginGetOptionToCredentialOptionMap),
                     filteredRequest,
                     getRequestSession.mClientAppInfo,
-                    beginGetOptionToCredentialOptionMap
+                    beginGetOptionToCredentialOptionMap,
+                    getRequestSession.mHybridService
             );
         }
         Log.i(TAG, "Unable to create provider session");
@@ -137,7 +134,7 @@
     }
 
     @Nullable
-    protected static android.credentials.GetCredentialRequest filterOptions(
+    private static android.credentials.GetCredentialRequest filterOptions(
             List<String> providerCapabilities,
             android.credentials.GetCredentialRequest clientRequest
     ) {
@@ -169,19 +166,22 @@
             BeginGetCredentialRequest beginGetRequest,
             android.credentials.GetCredentialRequest completeGetRequest,
             CallingAppInfo callingAppInfo,
-            Map<String, CredentialOption> beginGetOptionToCredentialOptionMap) {
+            Map<String, CredentialOption> beginGetOptionToCredentialOptionMap,
+            String hybridService) {
         super(context, info, beginGetRequest, callbacks, userId, remoteCredentialService);
         mCompleteRequest = completeGetRequest;
         mCallingAppInfo = callingAppInfo;
         setStatus(Status.PENDING);
         mBeginGetOptionToCredentialOptionMap = new HashMap<>(beginGetOptionToCredentialOptionMap);
+        mProviderResponseDataHandler = new ProviderResponseDataHandler(
+                ComponentName.unflattenFromString(hybridService));
     }
 
     /** Called when the provider response has been updated by an external source. */
     @Override // Callback from the remote provider
     public void onProviderResponseSuccess(@Nullable BeginGetCredentialResponse response) {
         Log.i(TAG, "in onProviderResponseSuccess");
-        onUpdateResponse(response);
+        onSetInitialRemoteResponse(response);
     }
 
     /** Called when the provider response resulted in a failure. */
@@ -207,18 +207,20 @@
     @Override // Selection call from the request provider
     protected void onUiEntrySelected(String entryType, String entryKey,
             ProviderPendingIntentResponse providerPendingIntentResponse) {
+        Log.i(TAG, "onUiEntrySelected with entryKey: " + entryKey);
         switch (entryType) {
             case CREDENTIAL_ENTRY_KEY:
-                CredentialEntry credentialEntry = mUiCredentialEntries.get(entryKey);
+                CredentialEntry credentialEntry = mProviderResponseDataHandler
+                        .getCredentialEntry(entryKey);
                 if (credentialEntry == null) {
                     Log.i(TAG, "Unexpected credential entry key");
                     invokeCallbackOnInternalInvalidState();
                     return;
                 }
-                onCredentialEntrySelected(credentialEntry, providerPendingIntentResponse);
+                onCredentialEntrySelected(providerPendingIntentResponse);
                 break;
             case ACTION_ENTRY_KEY:
-                Action actionEntry = mUiActionsEntries.get(entryKey);
+                Action actionEntry = mProviderResponseDataHandler.getActionEntry(entryKey);
                 if (actionEntry == null) {
                     Log.i(TAG, "Unexpected action entry key");
                     invokeCallbackOnInternalInvalidState();
@@ -227,16 +229,29 @@
                 onActionEntrySelected(providerPendingIntentResponse);
                 break;
             case AUTHENTICATION_ACTION_ENTRY_KEY:
-                Action authenticationEntry = mUiAuthenticationEntries.get(entryKey);
+                Action authenticationEntry = mProviderResponseDataHandler
+                        .getAuthenticationAction(entryKey);
                 if (authenticationEntry == null) {
                     Log.i(TAG, "Unexpected authenticationEntry key");
                     invokeCallbackOnInternalInvalidState();
                     return;
                 }
-                onAuthenticationEntrySelected(providerPendingIntentResponse);
+                boolean additionalContentReceived =
+                        onAuthenticationEntrySelected(providerPendingIntentResponse);
+                if (additionalContentReceived) {
+                    Log.i(TAG, "Additional content received - removing authentication entry");
+                    mProviderResponseDataHandler.removeAuthenticationAction(entryKey);
+                } else {
+                    Log.i(TAG, "Additional content not received");
+                    mProviderResponseDataHandler
+                            .updateAuthEntryWithNoCredentialsReceived(entryKey);
+                }
+                if (!mProviderResponseDataHandler.isEmptyResponse()) {
+                    updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
+                }
                 break;
             case REMOTE_ENTRY_KEY:
-                if (mUiRemoteEntry.first.equals(entryKey)) {
+                if (mProviderResponseDataHandler.getRemoteEntry(entryKey) != null) {
                     onRemoteEntrySelected(providerPendingIntentResponse);
                 } else {
                     Log.i(TAG, "Unexpected remote entry key");
@@ -245,6 +260,7 @@
                 break;
             default:
                 Log.i(TAG, "Unsupported entry type selected");
+                invokeCallbackOnInternalInvalidState();
         }
     }
 
@@ -252,6 +268,7 @@
     protected void invokeSession() {
         if (mRemoteCredentialService != null) {
             mRemoteCredentialService.onBeginGetCredential(mProviderRequest, this);
+            mCandidateProviderMetric.setStartTimeNanoseconds(System.nanoTime());
         }
     }
 
@@ -263,61 +280,11 @@
                     + mComponentName.flattenToString());
             return null;
         }
-        if (mProviderResponse == null) {
-            Log.i(TAG, "In prepareUiData response null");
-            throw new IllegalStateException("Response must be in completion mode");
+        if (mProviderResponse != null && !mProviderResponseDataHandler.isEmptyResponse()) {
+            return mProviderResponseDataHandler.toGetCredentialProviderData();
         }
-        return prepareUiProviderData(prepareUiActionEntries(
-                        mProviderResponse.getActions()),
-                prepareUiCredentialEntries(mProviderResponse.getCredentialEntries()),
-                prepareUiAuthenticationEntries(mProviderResponse.getAuthenticationActions()),
-                prepareUiRemoteEntry(mProviderResponse.getRemoteCredentialEntry()));
-    }
-
-    private Entry prepareUiRemoteEntry(CredentialEntry remoteCredentialEntry) {
-        if (remoteCredentialEntry == null) {
-            return null;
-        }
-        String entryId = generateUniqueId();
-        Entry remoteEntry = new Entry(REMOTE_ENTRY_KEY, entryId, remoteCredentialEntry.getSlice(),
-                setUpFillInIntent(remoteCredentialEntry.getType()));
-        mUiRemoteEntry = new Pair<>(entryId, remoteCredentialEntry);
-        return remoteEntry;
-    }
-
-    private List<AuthenticationEntry> prepareUiAuthenticationEntries(
-            @NonNull List<Action> authenticationEntries) {
-        List<AuthenticationEntry> authenticationUiEntries = new ArrayList<>();
-
-        // TODO: properly construct entries when they should have the unlocked status.
-        for (Action authenticationAction : authenticationEntries) {
-            String entryId = generateUniqueId();
-            mUiAuthenticationEntries.put(entryId, authenticationAction);
-            authenticationUiEntries.add(new AuthenticationEntry(
-                    AUTHENTICATION_ACTION_ENTRY_KEY, entryId,
-                    authenticationAction.getSlice(),
-                    AuthenticationEntry.STATUS_LOCKED,
-                    setUpFillInIntentForAuthentication()));
-        }
-        return authenticationUiEntries;
-    }
-
-    private List<Entry> prepareUiCredentialEntries(@NonNull
-            List<CredentialEntry> credentialEntries) {
-        Log.i(TAG, "in prepareUiProviderDataWithCredentials");
-        List<Entry> credentialUiEntries = new ArrayList<>();
-
-        // Populate the credential entries
-        for (CredentialEntry credentialEntry : credentialEntries) {
-            String entryId = generateUniqueId();
-            mUiCredentialEntries.put(entryId, credentialEntry);
-            Log.i(TAG, "in prepareUiProviderData creating ui entry with id " + entryId);
-            credentialUiEntries.add(new Entry(CREDENTIAL_ENTRY_KEY, entryId,
-                    credentialEntry.getSlice(),
-                    /*fillInIntent=*/setUpFillInIntent(credentialEntry
-                    .getBeginGetCredentialOption().getId())));
-        }
-        return credentialUiEntries;
+        Log.i(TAG, "In prepareUiData response null");
+        return null;
     }
 
     private Intent setUpFillInIntent(@NonNull String id) {
@@ -334,68 +301,47 @@
                         mCallingAppInfo, mBeginGetOptionToCredentialOptionMap.get(id)));
     }
 
-    private Intent setUpFillInIntentForAuthentication() {
+    private Intent setUpFillInIntentWithQueryRequest() {
         Intent intent = new Intent();
-        intent.putExtra(
-                CredentialProviderService
-                        .EXTRA_BEGIN_GET_CREDENTIAL_REQUEST,
+        intent.putExtra(CredentialProviderService.EXTRA_BEGIN_GET_CREDENTIAL_REQUEST,
                 mProviderRequest);
         return intent;
     }
 
-    private List<Entry> prepareUiActionEntries(@Nullable List<Action> actions) {
-        List<Entry> actionEntries = new ArrayList<>();
-        for (Action action : actions) {
-            String entryId = UUID.randomUUID().toString();
-            mUiActionsEntries.put(entryId, action);
-            // TODO : Remove conversion of string to int after change in Entry class
-            actionEntries.add(new Entry(ACTION_ENTRY_KEY, entryId, action.getSlice()));
-        }
-        return actionEntries;
-    }
-
-    private GetCredentialProviderData prepareUiProviderData(List<Entry> actionEntries,
-            List<Entry> credentialEntries, List<AuthenticationEntry> authenticationActionEntries,
-            Entry remoteEntry) {
-        return new GetCredentialProviderData.Builder(
-                mComponentName.flattenToString()).setActionChips(actionEntries)
-                .setCredentialEntries(credentialEntries)
-                .setAuthenticationEntries(authenticationActionEntries)
-                .setRemoteEntry(remoteEntry)
-                .build();
-    }
-
-    private void onCredentialEntrySelected(CredentialEntry credentialEntry,
+    private void onRemoteEntrySelected(
             ProviderPendingIntentResponse providerPendingIntentResponse) {
-        if (providerPendingIntentResponse != null) {
-            // Check if pending intent has an error
-            GetCredentialException exception = maybeGetPendingIntentException(
-                    providerPendingIntentResponse);
-            if (exception != null) {
-                invokeCallbackWithError(exception.getType(),
-                        exception.getMessage());
-                return;
-            }
+        onCredentialEntrySelected(providerPendingIntentResponse);
+    }
 
-            // Check if pending intent has a credential
-            GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
-                    .extractGetCredentialResponse(
-                            providerPendingIntentResponse.getResultData());
-            if (getCredentialResponse != null) {
-                mCallbacks.onFinalResponseReceived(mComponentName,
-                        getCredentialResponse);
-                return;
-            }
-
-            Log.i(TAG, "Pending intent response contains no credential, or error");
+    private void onCredentialEntrySelected(
+            ProviderPendingIntentResponse providerPendingIntentResponse) {
+        if (providerPendingIntentResponse == null) {
             invokeCallbackOnInternalInvalidState();
+            return;
         }
-        Log.i(TAG, "CredentialEntry does not have a credential or a pending intent result");
+        // Check if pending intent has an error
+        GetCredentialException exception = maybeGetPendingIntentException(
+                providerPendingIntentResponse);
+        if (exception != null) {
+            invokeCallbackWithError(exception.getType(), exception.getMessage());
+            return;
+        }
+
+        // Check if pending intent has a credential response
+        GetCredentialResponse getCredentialResponse = PendingIntentResultHandler
+                .extractGetCredentialResponse(
+                        providerPendingIntentResponse.getResultData());
+        if (getCredentialResponse != null) {
+            mCallbacks.onFinalResponseReceived(mComponentName,
+                    getCredentialResponse);
+            return;
+        }
+        Log.i(TAG, "Pending intent response contains no credential, or error");
         invokeCallbackOnInternalInvalidState();
     }
 
     @Nullable
-    protected GetCredentialException maybeGetPendingIntentException(
+    private GetCredentialException maybeGetPendingIntentException(
             ProviderPendingIntentResponse pendingIntentResponse) {
         if (pendingIntentResponse == null) {
             Log.i(TAG, "pendingIntentResponse is null");
@@ -416,13 +362,19 @@
         return null;
     }
 
-    private void onAuthenticationEntrySelected(
+    /**
+     * Returns true if either an exception or a response is retrieved from the result.
+     * Returns false if the response is not set at all, or set to null, or empty.
+     */
+    private boolean onAuthenticationEntrySelected(
             @Nullable ProviderPendingIntentResponse providerPendingIntentResponse) {
-        //TODO: Other provider intent statuses
+        Log.i(TAG, "onAuthenticationEntrySelected");
+        // Authentication entry is expected to have a BeginGetCredentialResponse instance. If it
+        // does not have it, we remove the authentication entry and do not add any more content.
         if (providerPendingIntentResponse == null) {
             Log.i(TAG, "providerPendingIntentResponse is null");
-            onUpdateEmptyResponse();
-            return;
+            // Nothing received. This is equivalent to no content received.
+            return false;
         }
 
         GetCredentialException exception = maybeGetPendingIntentException(
@@ -430,51 +382,69 @@
         if (exception != null) {
             invokeCallbackWithError(exception.getType(),
                     exception.getMessage());
-            return;
+            // Additional content received is in the form of an exception which ends the flow.
+            return true;
         }
-
-        // Check if pending intent has the content
-        BeginGetCredentialResponse content = PendingIntentResultHandler
+        // Check if pending intent has the response. If yes, remove this auth entry and
+        // replace it with the response content received.
+        BeginGetCredentialResponse response = PendingIntentResultHandler
                 .extractResponseContent(providerPendingIntentResponse
                         .getResultData());
-        if (content != null) {
-            onUpdateResponse(content);
+        if (response != null && !mProviderResponseDataHandler.isEmptyResponse(response)) {
+            addToInitialRemoteResponse(response);
+            // Additional content received is in the form of new response content.
+            return true;
+        }
+        // No response or exception found.
+        return false;
+    }
+
+    private void addToInitialRemoteResponse(BeginGetCredentialResponse content) {
+        if (content == null) {
+            return;
+        }
+        mProviderResponseDataHandler.addResponseContent(
+                content.getCredentialEntries(),
+                content.getActions(),
+                content.getAuthenticationActions(),
+                content.getRemoteCredentialEntry()
+        );
+    }
+
+    /** Returns true if either an exception or a response is found. */
+    private void onActionEntrySelected(ProviderPendingIntentResponse
+            providerPendingIntentResponse) {
+        // Action entry is expected to either contain the final GetCredentialResponse, or it is
+        // also acceptable if it does not contain anything. In the second case, we re-show this
+        // action on the UI.
+        if (providerPendingIntentResponse == null) {
+            Log.i(TAG, "providerPendingIntentResponse is null");
             return;
         }
 
-        Log.i(TAG, "No error or respond found in pending intent response");
-        onUpdateEmptyResponse();
-    }
-
-    private void onActionEntrySelected(ProviderPendingIntentResponse
-            providerPendingIntentResponse) {
-        //TODO: Implement if any result expected after an action
+        GetCredentialException exception = maybeGetPendingIntentException(
+                providerPendingIntentResponse);
+        if (exception != null) {
+            invokeCallbackWithError(exception.getType(), exception.getMessage());
+        }
+        GetCredentialResponse response = PendingIntentResultHandler
+                .extractGetCredentialResponse(
+                        providerPendingIntentResponse.getResultData());
+        if (response != null) {
+            mCallbacks.onFinalResponseReceived(mComponentName, response);
+        }
     }
 
 
     /** Updates the response being maintained in state by this provider session. */
-    private void onUpdateResponse(BeginGetCredentialResponse response) {
+    private void onSetInitialRemoteResponse(BeginGetCredentialResponse response) {
         mProviderResponse = response;
-        if (isEmptyResponse(response)) {
+        addToInitialRemoteResponse(response);
+        if (mProviderResponseDataHandler.isEmptyResponse(response)) {
             updateStatusAndInvokeCallback(Status.EMPTY_RESPONSE);
-        } else {
-            updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
+            return;
         }
-    }
-
-    private boolean isEmptyResponse(BeginGetCredentialResponse response) {
-        if ((response.getCredentialEntries() == null || response.getCredentialEntries().isEmpty())
-                && (response.getAuthenticationActions() == null || response
-                .getAuthenticationActions().isEmpty())
-                && (response.getActions() == null || response.getActions().isEmpty())
-                && response.getRemoteCredentialEntry() == null) {
-            return true;
-        }
-        return false;
-    }
-
-    private void onUpdateEmptyResponse() {
-        updateStatusAndInvokeCallback(Status.NO_CREDENTIALS);
+        updateStatusAndInvokeCallback(Status.CREDENTIALS_RECEIVED);
     }
 
     /**
@@ -485,4 +455,201 @@
         mCallbacks.onFinalErrorReceived(mComponentName,
                 GetCredentialException.TYPE_UNKNOWN, null);
     }
+
+    private class ProviderResponseDataHandler {
+        private final ComponentName mExpectedRemoteEntryProviderService;
+        @NonNull
+        private final Map<String, Pair<CredentialEntry, Entry>> mUiCredentialEntries =
+                new HashMap<>();
+        @NonNull
+        private final Map<String, Pair<Action, Entry>> mUiActionsEntries = new HashMap<>();
+        @Nullable
+        private final Map<String, Pair<Action, AuthenticationEntry>> mUiAuthenticationEntries =
+                new HashMap<>();
+
+        @Nullable private Pair<String, Pair<CredentialEntry, Entry>> mUiRemoteEntry = null;
+
+        ProviderResponseDataHandler(ComponentName expectedRemoteEntryProviderService) {
+            mExpectedRemoteEntryProviderService = expectedRemoteEntryProviderService;
+        }
+
+        public void addResponseContent(List<CredentialEntry> credentialEntries,
+                List<Action> actions, List<Action> authenticationActions,
+                CredentialEntry remoteEntry) {
+            credentialEntries.forEach(this::addCredentialEntry);
+            actions.forEach(this::addAction);
+            authenticationActions.forEach(
+                    authenticationAction -> addAuthenticationAction(authenticationAction,
+                            AuthenticationEntry.STATUS_LOCKED));
+            setRemoteEntry(remoteEntry);
+        }
+        public void addCredentialEntry(CredentialEntry credentialEntry) {
+            String id = generateUniqueId();
+            Entry entry = new Entry(CREDENTIAL_ENTRY_KEY,
+                    id, credentialEntry.getSlice(),
+                    setUpFillInIntent(credentialEntry
+                            .getBeginGetCredentialOption().getId()));
+            mUiCredentialEntries.put(id, new Pair<>(credentialEntry, entry));
+        }
+
+        public void addAction(Action action) {
+            String id = generateUniqueId();
+            Entry entry = new Entry(ACTION_ENTRY_KEY,
+                    id, action.getSlice(),
+                    setUpFillInIntentWithQueryRequest());
+            mUiActionsEntries.put(id, new Pair<>(action, entry));
+        }
+
+        public void addAuthenticationAction(Action authenticationAction,
+                @AuthenticationEntry.Status int status) {
+            Log.i(TAG, "In addAuthenticationAction");
+            String id = generateUniqueId();
+            Log.i(TAG, "In addAuthenticationAction, id : " + id);
+            AuthenticationEntry entry = new AuthenticationEntry(
+                    AUTHENTICATION_ACTION_ENTRY_KEY,
+                    id, authenticationAction.getSlice(),
+                    status,
+                    setUpFillInIntentWithQueryRequest());
+            mUiAuthenticationEntries.put(id, new Pair<>(authenticationAction, entry));
+        }
+
+        public void removeAuthenticationAction(String id) {
+            mUiAuthenticationEntries.remove(id);
+        }
+
+        public void setRemoteEntry(@Nullable CredentialEntry remoteEntry) {
+            if (remoteEntry == null) {
+                return;
+            }
+            if (!mComponentName.equals(mExpectedRemoteEntryProviderService)) {
+                Log.i(TAG, "Remote entry being dropped as it is not from the service "
+                        + "configured by the OEM.");
+                return;
+            }
+            String id = generateUniqueId();
+            Entry entry = new Entry(REMOTE_ENTRY_KEY,
+                    id, remoteEntry.getSlice(), setUpFillInIntent(
+                            remoteEntry.getBeginGetCredentialOption().getId()));
+            mUiRemoteEntry = new Pair<>(generateUniqueId(), new Pair<>(remoteEntry, entry));
+        }
+
+        public GetCredentialProviderData toGetCredentialProviderData() {
+            return new GetCredentialProviderData.Builder(
+                    mComponentName.flattenToString()).setActionChips(prepareActionEntries())
+                    .setCredentialEntries(prepareCredentialEntries())
+                    .setAuthenticationEntries(prepareAuthenticationEntries())
+                    .setRemoteEntry(prepareRemoteEntry())
+                    .build();
+        }
+
+        private List<Entry> prepareActionEntries() {
+            List<Entry> actionEntries = new ArrayList<>();
+            for (String key : mUiActionsEntries.keySet()) {
+                actionEntries.add(mUiActionsEntries.get(key).second);
+            }
+            return actionEntries;
+        }
+
+        private List<AuthenticationEntry> prepareAuthenticationEntries() {
+            List<AuthenticationEntry> authEntries = new ArrayList<>();
+            for (String key : mUiAuthenticationEntries.keySet()) {
+                authEntries.add(mUiAuthenticationEntries.get(key).second);
+            }
+            return authEntries;
+        }
+
+        private List<Entry> prepareCredentialEntries() {
+            List<Entry> credEntries = new ArrayList<>();
+            for (String key : mUiCredentialEntries.keySet()) {
+                credEntries.add(mUiCredentialEntries.get(key).second);
+            }
+            return credEntries;
+        }
+
+
+        private Entry prepareRemoteEntry() {
+            if (mUiRemoteEntry == null || mUiRemoteEntry.first == null
+                    || mUiRemoteEntry.second == null) {
+                return null;
+            }
+            return mUiRemoteEntry.second.second;
+        }
+
+        private boolean isEmptyResponse() {
+            return mUiCredentialEntries.isEmpty() && mUiActionsEntries.isEmpty()
+                    && mUiAuthenticationEntries.isEmpty() && mUiRemoteEntry == null;
+        }
+
+        private boolean isEmptyResponse(BeginGetCredentialResponse response) {
+            return response.getCredentialEntries().isEmpty() && response.getActions().isEmpty()
+                    && response.getAuthenticationActions().isEmpty()
+                    && response.getRemoteCredentialEntry() == null;
+        }
+
+        @Nullable
+        public Action getAuthenticationAction(String entryKey) {
+            return mUiAuthenticationEntries.get(entryKey) == null ? null :
+                    mUiAuthenticationEntries.get(entryKey).first;
+        }
+
+        @Nullable
+        public Action getActionEntry(String entryKey) {
+            return mUiActionsEntries.get(entryKey) == null
+                    ? null : mUiActionsEntries.get(entryKey).first;
+        }
+
+        @Nullable
+        public CredentialEntry getRemoteEntry(String entryKey) {
+            return mUiRemoteEntry.first.equals(entryKey) && mUiRemoteEntry.second != null
+                    ? mUiRemoteEntry.second.first : null;
+        }
+
+        @Nullable
+        public CredentialEntry getCredentialEntry(String entryKey) {
+            return mUiCredentialEntries.get(entryKey) == null
+                    ? null : mUiCredentialEntries.get(entryKey).first;
+        }
+
+        public void updateAuthEntryWithNoCredentialsReceived(String entryKey) {
+            updatePreviousMostRecentAuthEntry();
+            updateMostRecentAuthEntry(entryKey);
+        }
+
+        private void updateMostRecentAuthEntry(String entryKey) {
+            AuthenticationEntry previousAuthenticationEntry =
+                    mUiAuthenticationEntries.get(entryKey).second;
+            Action previousAuthenticationAction = mUiAuthenticationEntries.get(entryKey).first;
+            mUiAuthenticationEntries.put(entryKey, new Pair<>(
+                    previousAuthenticationAction,
+                    copyAuthEntryAndChangeStatus(
+                            previousAuthenticationEntry,
+                            AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT)));
+        }
+
+        private void updatePreviousMostRecentAuthEntry() {
+            Optional<Map.Entry<String, Pair<Action, AuthenticationEntry>>>
+                    previousMostRecentAuthEntry = mUiAuthenticationEntries
+                    .entrySet().stream().filter(e -> e.getValue().second.getStatus()
+                            == AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_MOST_RECENT)
+                    .findFirst();
+            if (previousMostRecentAuthEntry.isEmpty()) {
+                Log.i(TAG, "In updatePreviousMostRecentAuthEntry - previous entry not found");
+                return;
+            }
+            String id = previousMostRecentAuthEntry.get().getKey();
+            mUiAuthenticationEntries.remove(id);
+            mUiAuthenticationEntries.put(id, new Pair<>(
+                    previousMostRecentAuthEntry.get().getValue().first,
+                    copyAuthEntryAndChangeStatus(
+                            previousMostRecentAuthEntry.get().getValue().second,
+                            AuthenticationEntry.STATUS_UNLOCKED_BUT_EMPTY_LESS_RECENT)));
+        }
+
+        private AuthenticationEntry copyAuthEntryAndChangeStatus(
+                AuthenticationEntry from, Integer toStatus) {
+            return new AuthenticationEntry(AUTHENTICATION_ACTION_ENTRY_KEY, from.getSubkey(),
+                    from.getSlice(), toStatus,
+                    from.getFrameworkExtrasIntent());
+        }
+    }
 }
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index dd9fcb6..5af8080 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -235,6 +235,7 @@
                         -> filterResult.mCredentialEntries.stream())
                 .collect(Collectors.toList());
         setStatus(Status.CREDENTIALS_RECEIVED);
+        // TODO(use metric later)
     }
 
     @Nullable
diff --git a/services/credentials/java/com/android/server/credentials/ProviderSession.java b/services/credentials/java/com/android/server/credentials/ProviderSession.java
index 1aec934..2f9d578 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderSession.java
@@ -16,6 +16,9 @@
 
 package com.android.server.credentials;
 
+import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_FAILURE;
+import static com.android.server.credentials.MetricUtilities.METRICS_PROVIDER_STATUS_QUERY_SUCCESS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
@@ -28,6 +31,8 @@
 import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
+import com.android.server.credentials.metrics.CandidateProviderMetric;
+
 import java.util.UUID;
 
 /**
@@ -52,16 +57,16 @@
     @NonNull protected final T mProviderRequest;
     @Nullable protected R mProviderResponse;
     @NonNull protected Boolean mProviderResponseSet = false;
-
-
+    // Specific candidate provider metric for the provider this session handles
+    @Nullable protected CandidateProviderMetric mCandidateProviderMetric;
+    @NonNull private int mProviderSessionUid;
 
     /**
      * Returns true if the given status reflects that the provider state is ready to be shown
      * on the credMan UI.
      */
     public static boolean isUiInvokingStatus(Status status) {
-        return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED
-                || status == Status.REQUIRES_AUTHENTICATION;
+        return status == Status.CREDENTIALS_RECEIVED || status == Status.SAVE_ENTRIES_RECEIVED;
     }
 
     /**
@@ -119,6 +124,8 @@
         mUserId = userId;
         mComponentName = info.getServiceInfo().getComponentName();
         mRemoteCredentialService = remoteCredentialService;
+        mCandidateProviderMetric = new CandidateProviderMetric();
+        mProviderSessionUid = MetricUtilities.getPackageUid(mContext, mComponentName);
     }
 
     /** Provider status at various states of the request session. */
@@ -185,12 +192,19 @@
     /** Updates the status .*/
     protected void updateStatusAndInvokeCallback(@NonNull Status status) {
         setStatus(status);
+        updateCandidateMetric(status);
         mCallbacks.onProviderStatusChanged(status, mComponentName);
     }
 
-    protected void onRemoteEntrySelected(
-            ProviderPendingIntentResponse providerPendingIntentResponse) {
-        //TODO: Implement
+    private void updateCandidateMetric(Status status) {
+        mCandidateProviderMetric.setCandidateUid(mProviderSessionUid);
+        mCandidateProviderMetric
+                .setQueryFinishTimeNanoseconds(System.nanoTime());
+        if (isTerminatingStatus(status)) {
+            mCandidateProviderMetric.setProviderQueryStatus(METRICS_PROVIDER_STATUS_QUERY_FAILURE);
+        } else if (isCompletionStatus(status)) {
+            mCandidateProviderMetric.setProviderQueryStatus(METRICS_PROVIDER_STATUS_QUERY_SUCCESS);
+        }
     }
 
     /** Get the request to be sent to the provider. */
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index 2dea8bd..702261e 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -24,6 +24,7 @@
 import android.credentials.ClearCredentialStateException;
 import android.credentials.CreateCredentialException;
 import android.credentials.GetCredentialException;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.ICancellationSignal;
 import android.os.RemoteException;
@@ -55,7 +56,7 @@
  *
  * @hide
  */
-public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialProviderService>{
+public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialProviderService> {
 
     private static final String TAG = "RemoteCredentialService";
     /** Timeout for a single request. */
@@ -69,13 +70,16 @@
     /**
      * Callbacks to be invoked when the provider remote service responds with a
      * success or failure.
+     *
      * @param <T> the type of response expected from the provider
      */
     public interface ProviderCallbacks<T> {
         /** Called when a successful response is received from the remote provider. */
         void onProviderResponseSuccess(@Nullable T response);
+
         /** Called when a failure response is received from the remote provider. */
         void onProviderResponseFailure(int internalErrorCode, @Nullable Exception e);
+
         /** Called when the remote provider service dies. */
         void onProviderServiceDied(RemoteCredentialService service);
     }
@@ -95,7 +99,8 @@
     }
 
     /** Return the componentName of the service to be connected. */
-    @NonNull public ComponentName getComponentName() {
+    @NonNull
+    public ComponentName getComponentName() {
         return mComponentName;
     }
 
@@ -104,9 +109,11 @@
         unbind();
     }
 
-    /** Main entry point to be called for executing a getCredential call on the remote
+    /**
+     * Main entry point to be called for executing a getCredential call on the remote
      * provider service.
-     * @param request the request to be sent to the provider
+     *
+     * @param request  the request to be sent to the provider
      * @param callback the callback to be used to send back the provider response to the
      *                 {@link ProviderGetSession} class that maintains provider state
      */
@@ -120,30 +127,36 @@
         CompletableFuture<BeginGetCredentialResponse> connectThenExecute = postAsync(service -> {
             CompletableFuture<BeginGetCredentialResponse> getCredentials =
                     new CompletableFuture<>();
-            ICancellationSignal cancellationSignal =
-                    service.onBeginGetCredential(request,
-                            new IBeginGetCredentialCallback.Stub() {
-                        @Override
-                        public void onSuccess(BeginGetCredentialResponse response) {
-                            Log.i(TAG, "In onSuccess in RemoteCredentialService");
-                            getCredentials.complete(response);
-                        }
+            final long originalCallingUidToken = Binder.clearCallingIdentity();
+            try {
+                ICancellationSignal cancellationSignal =
+                        service.onBeginGetCredential(request,
+                                new IBeginGetCredentialCallback.Stub() {
+                                    @Override
+                                    public void onSuccess(BeginGetCredentialResponse response) {
+                                        Log.i(TAG, "In onSuccess in RemoteCredentialService");
+                                        getCredentials.complete(response);
+                                    }
 
-                        @Override
-                        public void onFailure(String errorType, CharSequence message) {
-                            Log.i(TAG, "In onFailure in RemoteCredentialService");
-                            String errorMsg = message == null ? "" : String.valueOf(message);
-                                getCredentials.completeExceptionally(
-                                        new GetCredentialException(errorType, errorMsg));
-                        }
-                    });
-            CompletableFuture<BeginGetCredentialResponse> future = futureRef.get();
-            if (future != null && future.isCancelled()) {
-                dispatchCancellationSignal(cancellationSignal);
-            } else {
-                cancellationSink.set(cancellationSignal);
+                                    @Override
+                                    public void onFailure(String errorType, CharSequence message) {
+                                        Log.i(TAG, "In onFailure in RemoteCredentialService");
+                                        String errorMsg = message == null ? "" : String.valueOf(
+                                                message);
+                                        getCredentials.completeExceptionally(
+                                                new GetCredentialException(errorType, errorMsg));
+                                    }
+                                });
+                CompletableFuture<BeginGetCredentialResponse> future = futureRef.get();
+                if (future != null && future.isCancelled()) {
+                    dispatchCancellationSignal(cancellationSignal);
+                } else {
+                    cancellationSink.set(cancellationSignal);
+                }
+                return getCredentials;
+            } finally {
+                Binder.restoreCallingIdentity(originalCallingUidToken);
             }
-            return getCredentials;
         }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
 
         futureRef.set(connectThenExecute);
@@ -153,9 +166,11 @@
         return cancellationSink.get();
     }
 
-    /** Main entry point to be called for executing a beginCreateCredential call on the remote
+    /**
+     * Main entry point to be called for executing a beginCreateCredential call on the remote
      * provider service.
-     * @param request the request to be sent to the provider
+     *
+     * @param request  the request to be sent to the provider
      * @param callback the callback to be used to send back the provider response to the
      *                 {@link ProviderCreateSession} class that maintains provider state
      */
@@ -168,32 +183,39 @@
 
         CompletableFuture<BeginCreateCredentialResponse> connectThenExecute =
                 postAsync(service -> {
-            CompletableFuture<BeginCreateCredentialResponse> createCredentialFuture =
-                    new CompletableFuture<>();
-            ICancellationSignal cancellationSignal = service.onBeginCreateCredential(
-                    request, new IBeginCreateCredentialCallback.Stub() {
-                        @Override
-                        public void onSuccess(BeginCreateCredentialResponse response) {
-                            Log.i(TAG, "In onSuccess onBeginCreateCredential "
-                                    + "in RemoteCredentialService");
-                            createCredentialFuture.complete(response);
-                        }
+                    CompletableFuture<BeginCreateCredentialResponse> createCredentialFuture =
+                            new CompletableFuture<>();
+                    final long originalCallingUidToken = Binder.clearCallingIdentity();
+                    try {
+                        ICancellationSignal cancellationSignal = service.onBeginCreateCredential(
+                                request, new IBeginCreateCredentialCallback.Stub() {
+                                    @Override
+                                    public void onSuccess(BeginCreateCredentialResponse response) {
+                                        Log.i(TAG, "In onSuccess onBeginCreateCredential "
+                                                + "in RemoteCredentialService");
+                                        createCredentialFuture.complete(response);
+                                    }
 
-                        @Override
-                        public void onFailure(String errorType, CharSequence message) {
-                            Log.i(TAG, "In onFailure in RemoteCredentialService");
-                            String errorMsg = message == null ? "" : String.valueOf(message);
-                            createCredentialFuture.completeExceptionally(
-                                    new CreateCredentialException(errorType, errorMsg));
-                        }});
-            CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get();
-            if (future != null && future.isCancelled()) {
-                dispatchCancellationSignal(cancellationSignal);
-            } else {
-                cancellationSink.set(cancellationSignal);
-            }
-            return createCredentialFuture;
-        }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
+                                    @Override
+                                    public void onFailure(String errorType, CharSequence message) {
+                                        Log.i(TAG, "In onFailure in RemoteCredentialService");
+                                        String errorMsg = message == null ? "" : String.valueOf(
+                                                message);
+                                        createCredentialFuture.completeExceptionally(
+                                                new CreateCredentialException(errorType, errorMsg));
+                                    }
+                                });
+                        CompletableFuture<BeginCreateCredentialResponse> future = futureRef.get();
+                        if (future != null && future.isCancelled()) {
+                            dispatchCancellationSignal(cancellationSignal);
+                        } else {
+                            cancellationSink.set(cancellationSignal);
+                        }
+                        return createCredentialFuture;
+                    } finally {
+                        Binder.restoreCallingIdentity(originalCallingUidToken);
+                    }
+                }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
 
         futureRef.set(connectThenExecute);
         connectThenExecute.whenComplete((result, error) -> Handler.getMain().post(() ->
@@ -202,9 +224,11 @@
         return cancellationSink.get();
     }
 
-    /** Main entry point to be called for executing a clearCredentialState call on the remote
+    /**
+     * Main entry point to be called for executing a clearCredentialState call on the remote
      * provider service.
-     * @param request the request to be sent to the provider
+     *
+     * @param request  the request to be sent to the provider
      * @param callback the callback to be used to send back the provider response to the
      *                 {@link ProviderClearSession} class that maintains provider state
      */
@@ -218,30 +242,37 @@
                 postAsync(service -> {
                     CompletableFuture<Void> clearCredentialFuture =
                             new CompletableFuture<>();
-                    ICancellationSignal cancellationSignal = service.onClearCredentialState(
-                            request, new IClearCredentialStateCallback.Stub() {
-                                @Override
-                                public void onSuccess() {
-                                    Log.i(TAG, "In onSuccess onClearCredentialState "
-                                            + "in RemoteCredentialService");
-                                    clearCredentialFuture.complete(null);
-                                }
+                    final long originalCallingUidToken = Binder.clearCallingIdentity();
+                    try {
+                        ICancellationSignal cancellationSignal = service.onClearCredentialState(
+                                request, new IClearCredentialStateCallback.Stub() {
+                                    @Override
+                                    public void onSuccess() {
+                                        Log.i(TAG, "In onSuccess onClearCredentialState "
+                                                + "in RemoteCredentialService");
+                                        clearCredentialFuture.complete(null);
+                                    }
 
-                                @Override
-                                public void onFailure(String errorType, CharSequence message) {
-                                    Log.i(TAG, "In onFailure in RemoteCredentialService");
-                                    String errorMsg = message == null ? "" :
-                                            String.valueOf(message);
-                                    clearCredentialFuture.completeExceptionally(
-                                            new ClearCredentialStateException(errorType, errorMsg));
-                                }});
-                    CompletableFuture<Void> future = futureRef.get();
-                    if (future != null && future.isCancelled()) {
-                        dispatchCancellationSignal(cancellationSignal);
-                    } else {
-                        cancellationSink.set(cancellationSignal);
+                                    @Override
+                                    public void onFailure(String errorType, CharSequence message) {
+                                        Log.i(TAG, "In onFailure in RemoteCredentialService");
+                                        String errorMsg = message == null ? "" :
+                                                String.valueOf(message);
+                                        clearCredentialFuture.completeExceptionally(
+                                                new ClearCredentialStateException(errorType,
+                                                        errorMsg));
+                                    }
+                                });
+                        CompletableFuture<Void> future = futureRef.get();
+                        if (future != null && future.isCancelled()) {
+                            dispatchCancellationSignal(cancellationSignal);
+                        } else {
+                            cancellationSink.set(cancellationSignal);
+                        }
+                        return clearCredentialFuture;
+                    } finally {
+                        Binder.restoreCallingIdentity(originalCallingUidToken);
                     }
-                    return clearCredentialFuture;
                 }).orTimeout(TIMEOUT_REQUEST_MILLIS, TimeUnit.MILLISECONDS);
 
         futureRef.set(connectThenExecute);
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index fdd0e81..8622665 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -16,15 +16,16 @@
 
 package com.android.server.credentials;
 
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
-import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
+import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_CLEAR_CREDENTIAL;
+import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_CREATE_CREDENTIAL;
+import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_GET_CREDENTIAL;
+import static com.android.server.credentials.MetricUtilities.METRICS_API_NAME_UNKNOWN;
+import static com.android.server.credentials.MetricUtilities.METRICS_API_STATUS_FAILURE;
+import static com.android.server.credentials.MetricUtilities.METRICS_API_STATUS_SUCCESS;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.content.ComponentName;
 import android.content.Context;
 import android.credentials.ui.ProviderData;
 import android.credentials.ui.UserSelectionDialogResult;
@@ -37,7 +38,10 @@
 import android.service.credentials.CredentialProviderInfo;
 import android.util.Log;
 
+import com.android.internal.R;
 import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.credentials.metrics.CandidateProviderMetric;
+import com.android.server.credentials.metrics.ChosenProviderMetric;
 
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -50,20 +54,6 @@
 abstract class RequestSession<T, U> implements CredentialManagerUi.CredentialManagerUiCallback {
     private static final String TAG = "RequestSession";
 
-    // Metrics constants
-    private static final int METRICS_API_NAME_UNKNOWN =
-            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_UNKNOWN;
-    private static final int METRICS_API_NAME_GET_CREDENTIAL =
-            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_GET_CREDENTIAL;
-    private static final int METRICS_API_NAME_CREATE_CREDENTIAL =
-            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CREATE_CREDENTIAL;
-    private static final int METRICS_API_NAME_CLEAR_CREDENTIAL =
-            CREDENTIAL_MANAGER_API_CALLED__API_NAME__API_NAME_CLEAR_CREDENTIAL;
-    private static final int METRICS_API_STATUS_SUCCESS =
-            CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_SUCCESS;
-    private static final int METRICS_API_STATUS_FAILURE =
-            CREDENTIAL_MANAGER_API_CALLED__API_STATUS__API_STATUS_FAILURE;
-
     // TODO: Revise access levels of attributes
     @NonNull
     protected final T mClientRequest;
@@ -88,6 +78,9 @@
     protected final CancellationSignal mCancellationSignal;
 
     protected final Map<String, ProviderSession> mProviders = new HashMap<>();
+    protected ChosenProviderMetric mChosenProviderMetric = new ChosenProviderMetric();
+    //TODO improve design to allow grouped metrics per request
+    protected final String mHybridService;
 
     protected RequestSession(@NonNull Context context,
             @UserIdInt int userId, int callingUid, @NonNull T clientRequest, U clientCallback,
@@ -106,6 +99,8 @@
         mRequestId = new Binder();
         mCredentialManagerUi = new CredentialManagerUi(mContext,
                 mUserId, this);
+        mHybridService = context.getResources().getString(
+                R.string.config_defaultCredentialManagerHybridService);
     }
 
     public abstract ProviderSession initiateProviderSession(CredentialProviderInfo providerInfo,
@@ -165,7 +160,6 @@
         }
         return false;
     }
-
     // TODO: move these definitions to a separate logging focused class.
     enum RequestType {
         GET_CREDENTIALS,
@@ -186,11 +180,30 @@
         }
     }
 
-    protected void logApiCalled(RequestType requestType, boolean isSuccessful) {
+    protected void logApiCalled(RequestType requestType, boolean isSuccessfulOverall) {
+        var providerSessions = mProviders.values();
+        int providerSize = providerSessions.size();
+        int[] candidateUidList = new int[providerSize];
+        int[] candidateQueryRoundTripTimeList = new int[providerSize];
+        int[] candidateStatusList = new int[providerSize];
+        int index = 0;
+        for (var session : providerSessions) {
+            CandidateProviderMetric metric = session.mCandidateProviderMetric;
+            candidateUidList[index] = metric.getCandidateUid();
+            candidateQueryRoundTripTimeList[index] = metric.getQueryLatencyMs();
+            candidateStatusList[index] = metric.getProviderQueryStatus();
+            index++;
+        }
         FrameworkStatsLog.write(FrameworkStatsLog.CREDENTIAL_MANAGER_API_CALLED,
                 /* api_name */getApiNameFromRequestType(requestType), /* caller_uid */
                 mCallingUid, /* api_status */
-                isSuccessful ? METRICS_API_STATUS_SUCCESS : METRICS_API_STATUS_FAILURE);
+                isSuccessfulOverall ? METRICS_API_STATUS_SUCCESS : METRICS_API_STATUS_FAILURE,
+                candidateUidList,
+                candidateQueryRoundTripTimeList,
+                candidateStatusList, mChosenProviderMetric.getChosenUid(),
+                mChosenProviderMetric.getEntireProviderLatencyMs(),
+                mChosenProviderMetric.getFinalPhaseLatencyMs(),
+                mChosenProviderMetric.getChosenProviderStatus());
     }
 
     protected boolean isSessionCancelled() {
@@ -239,4 +252,18 @@
             }
         }
     }
+
+    /**
+     * Called by RequestSession's upon chosen metric determination.
+     * @param componentName the componentName to associate with a provider
+     */
+    protected void setChosenMetric(ComponentName componentName) {
+        CandidateProviderMetric metric = this.mProviders.get(componentName.flattenToString())
+                .mCandidateProviderMetric;
+        mChosenProviderMetric.setChosenUid(metric.getCandidateUid());
+        mChosenProviderMetric.setFinalFinishTimeNanoseconds(System.nanoTime());
+        mChosenProviderMetric.setQueryFinishTimeNanoseconds(
+                metric.getQueryFinishTimeNanoseconds());
+        mChosenProviderMetric.setStartTimeNanoseconds(metric.getStartTimeNanoseconds());
+    }
 }
diff --git a/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
new file mode 100644
index 0000000..acfb4a4
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/CandidateProviderMetric.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 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.credentials.metrics;
+
+/**
+ * The central candidate provider metric object that mimics our defined metric setup.
+ */
+public class CandidateProviderMetric {
+
+    private int mCandidateUid = -1;
+    private long mStartTimeNanoseconds = -1;
+    private long mQueryFinishTimeNanoseconds = -1;
+
+    private int mProviderQueryStatus = -1;
+
+    public CandidateProviderMetric(long startTime, long queryFinishTime, int providerQueryStatus,
+            int candidateUid) {
+        this.mStartTimeNanoseconds = startTime;
+        this.mQueryFinishTimeNanoseconds = queryFinishTime;
+        this.mProviderQueryStatus = providerQueryStatus;
+        this.mCandidateUid = candidateUid;
+    }
+
+    public CandidateProviderMetric(){}
+
+    public void setStartTimeNanoseconds(long startTimeNanoseconds) {
+        this.mStartTimeNanoseconds = startTimeNanoseconds;
+    }
+
+    public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
+        this.mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+    }
+
+    public void setProviderQueryStatus(int providerQueryStatus) {
+        this.mProviderQueryStatus = providerQueryStatus;
+    }
+
+    public void setCandidateUid(int candidateUid) {
+        this.mCandidateUid = candidateUid;
+    }
+
+    public long getStartTimeNanoseconds() {
+        return this.mStartTimeNanoseconds;
+    }
+
+    public long getQueryFinishTimeNanoseconds() {
+        return this.mQueryFinishTimeNanoseconds;
+    }
+
+    public int getProviderQueryStatus() {
+        return this.mProviderQueryStatus;
+    }
+
+    public int getCandidateUid() {
+        return this.mCandidateUid;
+    }
+
+    /**
+     * Returns the latency in microseconds for the query phase.
+     */
+    public int getQueryLatencyMs() {
+        return (int) ((this.getQueryFinishTimeNanoseconds()
+                - this.getStartTimeNanoseconds()) / 1000);
+    }
+
+}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
new file mode 100644
index 0000000..c4d0b3c
--- /dev/null
+++ b/services/credentials/java/com/android/server/credentials/metrics/ChosenProviderMetric.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 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.credentials.metrics;
+
+/**
+ * The central chosen provider metric object that mimics our defined metric setup.
+ */
+public class ChosenProviderMetric {
+
+    private int mChosenUid = -1;
+    private long mStartTimeNanoseconds = -1;
+    private long mQueryFinishTimeNanoseconds = -1;
+    private long mFinalFinishTimeNanoseconds = -1;
+    private int mChosenProviderStatus = -1;
+
+    public ChosenProviderMetric() {}
+
+    public int getChosenUid() {
+        return mChosenUid;
+    }
+
+    public void setChosenUid(int chosenUid) {
+        mChosenUid = chosenUid;
+    }
+
+    public long getStartTimeNanoseconds() {
+        return mStartTimeNanoseconds;
+    }
+
+    public void setStartTimeNanoseconds(long startTimeNanoseconds) {
+        mStartTimeNanoseconds = startTimeNanoseconds;
+    }
+
+    public long getQueryFinishTimeNanoseconds() {
+        return mQueryFinishTimeNanoseconds;
+    }
+
+    public void setQueryFinishTimeNanoseconds(long queryFinishTimeNanoseconds) {
+        mQueryFinishTimeNanoseconds = queryFinishTimeNanoseconds;
+    }
+
+    public long getFinalFinishTimeNanoseconds() {
+        return mFinalFinishTimeNanoseconds;
+    }
+
+    public void setFinalFinishTimeNanoseconds(long finalFinishTimeNanoseconds) {
+        mFinalFinishTimeNanoseconds = finalFinishTimeNanoseconds;
+    }
+
+    public int getChosenProviderStatus() {
+        return mChosenProviderStatus;
+    }
+
+    public void setChosenProviderStatus(int chosenProviderStatus) {
+        mChosenProviderStatus = chosenProviderStatus;
+    }
+
+    /**
+     * Returns the full provider (invocation to response) latency in microseconds.
+     */
+    public int getEntireProviderLatencyMs() {
+        return (int) ((this.getFinalFinishTimeNanoseconds()
+                - this.getStartTimeNanoseconds()) / 1000);
+    }
+
+    // TODO get post click final phase and re-add the query phase time to metric
+
+    /**
+     * Returns the end of query to response phase latency in microseconds.
+     */
+    public int getFinalPhaseLatencyMs() {
+        return (int) ((this.getFinalFinishTimeNanoseconds()
+                - this.getQueryFinishTimeNanoseconds()) / 1000);
+    }
+
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 71764dc..8f16737 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -555,8 +555,9 @@
             if (!hasLocalPolicyLocked(policyDefinition, userId)) {
                 return null;
             }
-            return getLocalPolicyStateLocked(policyDefinition, userId)
-                    .getPoliciesSetByAdmins().get(enforcingAdmin).getValue();
+            PolicyValue<V> value = getLocalPolicyStateLocked(policyDefinition, userId)
+                    .getPoliciesSetByAdmins().get(enforcingAdmin);
+            return value == null ? null : value.getValue();
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f5a1b80..cdcfaba 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -33,6 +33,7 @@
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION;
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY;
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
+import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
 import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
@@ -61,6 +62,7 @@
 import static android.app.admin.DevicePolicyManager.EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION;
 import static android.app.admin.DevicePolicyManager.EXEMPT_FROM_APP_STANDBY;
 import static android.app.admin.DevicePolicyManager.EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
+import static android.app.admin.DevicePolicyManager.EXEMPT_FROM_HIBERNATION;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_IDS;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE;
@@ -235,6 +237,7 @@
 import android.app.admin.IntegerPolicyValue;
 import android.app.admin.IntentFilterPolicyKey;
 import android.app.admin.LockTaskPolicy;
+import android.app.admin.LongPolicyValue;
 import android.app.admin.ManagedProfileProvisioningParams;
 import android.app.admin.ManagedSubscriptionsPolicy;
 import android.app.admin.NetworkEvent;
@@ -726,6 +729,8 @@
         APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.put(
                 EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION,
                 OPSTR_SYSTEM_EXEMPT_FROM_ACTIVITY_BG_START_RESTRICTION);
+        APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.put(
+                EXEMPT_FROM_HIBERNATION, OPSTR_SYSTEM_EXEMPT_FROM_HIBERNATION);
     }
 
     /**
@@ -9038,12 +9043,12 @@
     }
 
     private @UserIdInt int getMainUserId() {
-        UserHandle mainUser = mUserManager.getMainUser();
-        if (mainUser == null) {
+        int mainUserId = mUserManagerInternal.getMainUserId();
+        if (mainUserId == UserHandle.USER_NULL) {
             Slogf.d(LOG_TAG, "getMainUserId(): no main user, returning USER_SYSTEM");
             return UserHandle.USER_SYSTEM;
         }
-        return mainUser.getIdentifier();
+        return mainUserId;
     }
 
     // TODO(b/240562946): Remove api as owner name is not used.
@@ -12019,10 +12024,10 @@
             Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
         }
 
-        int userHandle = caller.getUserId();
+        int userId = caller.getUserId();
         synchronized (getLockObject()) {
             final ActiveAdmin activeAdmin = getParentOfAdminIfRequired(
-                    getProfileOwnerOrDeviceOwnerLocked(userHandle), parent);
+                    getProfileOwnerOrDeviceOwnerLocked(userId), parent);
 
             if (isDefaultDeviceOwner(caller)) {
                 if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) {
@@ -12039,7 +12044,8 @@
                         "Cannot use the parent instance in Financed Device Owner mode");
             } else {
                 boolean profileOwnerCanChangeOnItself = !parent
-                        && UserRestrictionsUtils.canProfileOwnerChange(key, userHandle);
+                        && UserRestrictionsUtils.canProfileOwnerChange(
+                                key, userId == getMainUserId());
                 boolean orgOwnedProfileOwnerCanChangesGlobally = parent
                         && isProfileOwnerOfOrganizationOwnedDevice(caller)
                         && UserRestrictionsUtils.canProfileOwnerOfOrganizationOwnedDeviceChange(
@@ -12058,7 +12064,7 @@
             } else {
                 restrictions.remove(key);
             }
-            saveUserRestrictionsLocked(userHandle);
+            saveUserRestrictionsLocked(userId);
         }
         final int eventId = enabledFromThisOwner
                 ? DevicePolicyEnums.ADD_USER_RESTRICTION
@@ -12072,7 +12078,7 @@
             final int eventTag = enabledFromThisOwner
                     ? SecurityLog.TAG_USER_RESTRICTION_ADDED
                     : SecurityLog.TAG_USER_RESTRICTION_REMOVED;
-            SecurityLog.writeEvent(eventTag, who.getPackageName(), userHandle, key);
+            SecurityLog.writeEvent(eventTag, who.getPackageName(), userId, key);
         }
     }
 
@@ -16996,23 +17002,52 @@
         final CallerIdentity caller = getCallerIdentity(admin);
         Preconditions.checkCallAuthorization(
                 isProfileOwner(caller) || isDefaultDeviceOwner(caller));
+        final int userId = caller.getUserId();
 
-        synchronized (getLockObject()) {
-            final int userHandle = caller.getUserId();
-
-            DevicePolicyData policy = getUserData(userHandle);
-            return mInjector.binderWithCleanCallingIdentity(() -> {
-                if (policy.mPasswordTokenHandle != 0) {
-                    mLockPatternUtils.removeEscrowToken(policy.mPasswordTokenHandle, userHandle);
-                }
-                policy.mPasswordTokenHandle = mLockPatternUtils.addEscrowToken(token,
-                        userHandle, /*EscrowTokenStateChangeCallback*/ null);
-                saveSettingsLocked(userHandle);
+        if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                    admin, userId);
+            Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                    PolicyDefinition.RESET_PASSWORD_TOKEN,
+                    enforcingAdmin,
+                    userId);
+            long tokenHandle = addEscrowToken(
+                    token, currentTokenHandle == null ? 0 : currentTokenHandle, userId);
+            if (tokenHandle == 0) {
+                return false;
+            }
+            mDevicePolicyEngine.setLocalPolicy(
+                    PolicyDefinition.RESET_PASSWORD_TOKEN,
+                    enforcingAdmin,
+                    new LongPolicyValue(tokenHandle),
+                    userId);
+            return true;
+        } else {
+            synchronized (getLockObject()) {
+                DevicePolicyData policy = getUserData(userId);
+                policy.mPasswordTokenHandle = addEscrowToken(
+                        token, policy.mPasswordTokenHandle, userId);
+                saveSettingsLocked(userId);
                 return policy.mPasswordTokenHandle != 0;
-            });
+            }
         }
     }
 
+    private long addEscrowToken(byte[] token, long currentPasswordTokenHandle, int userId) {
+        resetEscrowToken(currentPasswordTokenHandle, userId);
+        return mInjector.binderWithCleanCallingIdentity(() -> mLockPatternUtils.addEscrowToken(
+                token, userId, /* EscrowTokenStateChangeCallback= */ null));
+    }
+
+    private boolean resetEscrowToken(long tokenHandle, int userId) {
+        return mInjector.binderWithCleanCallingIdentity(() -> {
+            if (tokenHandle != 0) {
+                return mLockPatternUtils.removeEscrowToken(tokenHandle, userId);
+            }
+            return false;
+        });
+    }
+
     @Override
     public boolean clearResetPasswordToken(ComponentName admin) {
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
@@ -17021,22 +17056,34 @@
         final CallerIdentity caller = getCallerIdentity(admin);
         Preconditions.checkCallAuthorization(
                 isProfileOwner(caller) || isDefaultDeviceOwner(caller));
+        final int userId = caller.getUserId();
+        boolean result = false;
 
-        synchronized (getLockObject()) {
-            final int userHandle = caller.getUserId();
-
-            DevicePolicyData policy = getUserData(userHandle);
-            if (policy.mPasswordTokenHandle != 0) {
-                return mInjector.binderWithCleanCallingIdentity(() -> {
-                    boolean result = mLockPatternUtils.removeEscrowToken(
-                            policy.mPasswordTokenHandle, userHandle);
+        if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                    admin, userId);
+            Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                    PolicyDefinition.RESET_PASSWORD_TOKEN,
+                    enforcingAdmin,
+                    userId);
+            if (currentTokenHandle != null) {
+                result = resetEscrowToken(currentTokenHandle, userId);
+                mDevicePolicyEngine.removeLocalPolicy(
+                        PolicyDefinition.RESET_PASSWORD_TOKEN,
+                        enforcingAdmin,
+                        userId);
+            }
+        } else {
+            synchronized (getLockObject()) {
+                DevicePolicyData policy = getUserData(userId);
+                if (policy.mPasswordTokenHandle != 0) {
+                    result = resetEscrowToken(policy.mPasswordTokenHandle, userId);
                     policy.mPasswordTokenHandle = 0;
-                    saveSettingsLocked(userHandle);
-                    return result;
-                });
+                    saveSettingsLocked(userId);
+                }
             }
         }
-        return false;
+        return result;
     }
 
     @Override
@@ -17048,16 +17095,30 @@
         Preconditions.checkCallAuthorization(
                 isProfileOwner(caller) || isDefaultDeviceOwner(caller));
 
-        synchronized (getLockObject()) {
-            return isResetPasswordTokenActiveForUserLocked(caller.getUserId());
+        int userId = caller.getUserId();
+
+        if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                    admin, userId);
+            Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                    PolicyDefinition.RESET_PASSWORD_TOKEN,
+                    enforcingAdmin,
+                    userId);
+            return isResetPasswordTokenActiveForUserLocked(
+                    currentTokenHandle == null ? 0 : currentTokenHandle, userId);
+        } else {
+            synchronized (getLockObject()) {
+                DevicePolicyData policy = getUserData(userId);
+                return isResetPasswordTokenActiveForUserLocked(policy.mPasswordTokenHandle, userId);
+            }
         }
     }
 
-    private boolean isResetPasswordTokenActiveForUserLocked(int userHandle) {
-        DevicePolicyData policy = getUserData(userHandle);
-        if (policy.mPasswordTokenHandle != 0) {
+    private boolean isResetPasswordTokenActiveForUserLocked(
+            long passwordTokenHandle, int userHandle) {
+        if (passwordTokenHandle != 0) {
             return mInjector.binderWithCleanCallingIdentity(() ->
-                    mLockPatternUtils.isEscrowTokenActive(policy.mPasswordTokenHandle, userHandle));
+                    mLockPatternUtils.isEscrowTokenActive(passwordTokenHandle, userHandle));
         }
         return false;
     }
@@ -17074,24 +17135,41 @@
         Preconditions.checkCallAuthorization(
                 isProfileOwner(caller) || isDefaultDeviceOwner(caller));
 
-        synchronized (getLockObject()) {
-            DevicePolicyData policy = getUserData(caller.getUserId());
-            if (policy.mPasswordTokenHandle != 0) {
-                final String password = passwordOrNull != null ? passwordOrNull : "";
-                final boolean result = resetPasswordInternal(password, policy.mPasswordTokenHandle,
-                        token, flags, caller);
-                if (result) {
-                    DevicePolicyEventLogger
-                            .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
-                            .setAdmin(caller.getComponentName())
-                            .write();
-                }
-                return result;
+        int userId = caller.getUserId();
+        boolean result = false;
+        final String password = passwordOrNull != null ? passwordOrNull : "";
+
+        if (useDevicePolicyEngine(caller, /* delegateScope= */ null)) {
+            EnforcingAdmin enforcingAdmin = EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                    admin, userId);
+            Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
+                    PolicyDefinition.RESET_PASSWORD_TOKEN,
+                    enforcingAdmin,
+                    userId);
+            if (currentTokenHandle != null && currentTokenHandle != 0) {
+                result = resetPasswordInternal(password, currentTokenHandle, token, flags, caller);
             } else {
                 Slogf.w(LOG_TAG, "No saved token handle");
             }
+        } else {
+            synchronized (getLockObject()) {
+                DevicePolicyData policy = getUserData(userId);
+                if (policy.mPasswordTokenHandle != 0) {
+                    result = resetPasswordInternal(
+                            password, policy.mPasswordTokenHandle, token, flags, caller);
+                } else {
+                    Slogf.w(LOG_TAG, "No saved token handle");
+                }
+            }
         }
-        return false;
+
+        if (result) {
+            DevicePolicyEventLogger
+                    .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
+                    .setAdmin(caller.getComponentName())
+                    .write();
+        }
+        return result;
     }
 
     @Override
@@ -18784,9 +18862,11 @@
                         "call canProfileOwnerResetPasswordWhenLocked"));
         synchronized (getLockObject()) {
             final ActiveAdmin poAdmin = getProfileOwnerAdminLocked(userId);
+            DevicePolicyData policy = getUserData(userId);
             if (poAdmin == null
                     || getEncryptionStatus() != ENCRYPTION_STATUS_ACTIVE_PER_USER
-                    || !isResetPasswordTokenActiveForUserLocked(userId)) {
+                    || !isResetPasswordTokenActiveForUserLocked(
+                            policy.mPasswordTokenHandle, userId)) {
                 return false;
             }
             final ApplicationInfo poAppInfo;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/LongPolicySerializer.java b/services/devicepolicy/java/com/android/server/devicepolicy/LongPolicySerializer.java
new file mode 100644
index 0000000..f77d051
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/LongPolicySerializer.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.devicepolicy;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.admin.LongPolicyValue;
+import android.app.admin.PolicyKey;
+import android.util.Log;
+
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Objects;
+
+final class LongPolicySerializer extends PolicySerializer<Long> {
+
+    @Override
+    void saveToXml(PolicyKey policyKey, TypedXmlSerializer serializer, String attributeName,
+            @NonNull Long value) throws IOException {
+        Objects.requireNonNull(value);
+        serializer.attributeLong(/* namespace= */ null, attributeName, value);
+    }
+
+    @Nullable
+    @Override
+    LongPolicyValue readFromXml(TypedXmlPullParser parser, String attributeName) {
+        try {
+            return new LongPolicyValue(
+                    parser.getAttributeLong(/* namespace= */ null, attributeName));
+        } catch (XmlPullParserException e) {
+            Log.e(DevicePolicyEngine.TAG, "Error parsing Long policy value", e);
+            return null;
+        }
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index ab6f732..9e0da26 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -199,6 +199,8 @@
             new PolicyDefinition<>(
                     new PackagePolicyKey(
                             DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY),
+                    // Don't need to take in a resolution mechanism since its never used, but might
+                    // need some refactoring to not always assume a non-null mechanism.
                     new MostRecent<>(),
                     POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY,
                     // Application restrictions are now stored and retrieved from DPMS, so no
@@ -221,6 +223,16 @@
                         DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY, packageName));
     }
 
+    static PolicyDefinition<Long> RESET_PASSWORD_TOKEN = new PolicyDefinition<>(
+            new NoArgsPolicyKey(DevicePolicyManager.RESET_PASSWORD_TOKEN_POLICY),
+            // Don't need to take in a resolution mechanism since its never used, but might
+            // need some refactoring to not always assume a non-null mechanism.
+            new MostRecent<>(),
+            POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_NON_COEXISTABLE_POLICY,
+            // DevicePolicyManagerService handles the enforcement, this just takes care of storage
+            (Long value, Context context, Integer userId, PolicyKey policyKey) -> true,
+            new LongPolicySerializer());
+
     private static final Map<String, PolicyDefinition<?>> sPolicyDefinitions = Map.of(
             DevicePolicyManager.AUTO_TIMEZONE_POLICY, AUTO_TIMEZONE,
             DevicePolicyManager.PERMISSION_GRANT_POLICY, GENERIC_PERMISSION_GRANT,
@@ -230,7 +242,8 @@
             DevicePolicyManager.PERSISTENT_PREFERRED_ACTIVITY_POLICY,
             GENERIC_PERSISTENT_PREFERRED_ACTIVITY,
             DevicePolicyManager.PACKAGE_UNINSTALL_BLOCKED_POLICY, GENERIC_PACKAGE_UNINSTALL_BLOCKED,
-            DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY, GENERIC_APPLICATION_RESTRICTIONS
+            DevicePolicyManager.APPLICATION_RESTRICTIONS_POLICY, GENERIC_APPLICATION_RESTRICTIONS,
+            DevicePolicyManager.RESET_PASSWORD_TOKEN_POLICY, RESET_PASSWORD_TOKEN
     );
 
 
diff --git a/services/java/com/android/server/HsumBootUserInitializer.java b/services/java/com/android/server/HsumBootUserInitializer.java
index a1853b4..50113fe 100644
--- a/services/java/com/android/server/HsumBootUserInitializer.java
+++ b/services/java/com/android/server/HsumBootUserInitializer.java
@@ -113,11 +113,7 @@
                     UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN,
                     /* disallowedPackages= */ null,
                     /* token= */ null);
-            if (newInitialUser == null) {
-                Slogf.wtf(TAG, "Initial bootable MainUser creation failed: returned null");
-            } else {
-                Slogf.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id);
-            }
+            Slogf.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id);
         } catch (UserManager.CheckedUserOperationException e) {
             Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e);
         }
diff --git a/services/proguard.flags b/services/proguard.flags
index c133044..c31abbb 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -95,7 +95,7 @@
 -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.pm.PackageManagerShellCommandDataLoader { *; }
--keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$RuntimeSensorStateChangeCallback { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$RuntimeSensorCallback { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; }
 -keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index 419351d4..9263bff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -711,8 +711,6 @@
                 null);                                // description
 
         // Case 8: App1 gets "remove task"
-        final String app1Description = "remove task";
-
         sleep(1);
         final int app1IsolatedUidUser2 = 1099002; // isolated uid
         final long app1Pss4 = 34343;
@@ -739,7 +737,7 @@
 
         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2);
         noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
-                ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description, now8);
+                ApplicationExitInfo.SUBREASON_REMOVE_TASK, null, now8);
 
         updateExitInfo(app, now8);
         list.clear();
@@ -749,21 +747,21 @@
         info = list.get(0);
 
         verifyApplicationExitInfo(
-                info,                                     // info
-                now8,                                     // timestamp
-                app1PidUser2,                             // pid
-                app1IsolatedUidUser2,                     // uid
-                app1UidUser2,                             // packageUid
-                null,                                     // definingUid
-                app1ProcessName,                          // processName
-                0,                                        // connectionGroup
-                ApplicationExitInfo.REASON_OTHER,         // reason
-                ApplicationExitInfo.SUBREASON_UNKNOWN,    // subReason
-                0,                                        // status
-                app1Pss4,                                 // pss
-                app1Rss4,                                 // rss
-                IMPORTANCE_CACHED,                        // importance
-                app1Description);                         // description
+                info,                                       // info
+                now8,                                       // timestamp
+                app1PidUser2,                               // pid
+                app1IsolatedUidUser2,                       // uid
+                app1UidUser2,                               // packageUid
+                null,                                       // definingUid
+                app1ProcessName,                            // processName
+                0,                                          // connectionGroup
+                ApplicationExitInfo.REASON_OTHER,           // reason
+                ApplicationExitInfo.SUBREASON_REMOVE_TASK,  // subReason
+                0,                                          // status
+                app1Pss4,                                   // pss
+                app1Rss4,                                   // rss
+                IMPORTANCE_CACHED,                          // importance
+                null);                                      // description
 
         // App1 gets "too many empty"
         final String app1Description2 = "too many empty";
@@ -1058,7 +1056,18 @@
         if (importance != null) {
             assertEquals(importance.intValue(), info.getImportance());
         }
-        if (description != null) {
+
+        // info.getDescription returns a combination of subReason & description
+        if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)
+                && (description != null)) {
+            assertTrue(TextUtils.equals(
+                    "[" + info.subreasonToString(subReason) + "] " + description,
+                    info.getDescription()));
+        } else if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)) {
+            assertTrue(TextUtils.equals(
+                    "[" + info.subreasonToString(subReason) + "]",
+                    info.getDescription()));
+        } else if (description != null) {
             assertTrue(TextUtils.equals(description, info.getDescription()));
         }
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 458c9cfc..6bc2d1f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -20,6 +20,7 @@
 
 import static com.android.server.am.BroadcastProcessQueue.reasonToString;
 import static com.android.server.am.BroadcastRecord.deliveryStateToString;
+import static com.android.server.am.BroadcastRecord.isReceiverEquals;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -76,8 +77,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -160,7 +159,7 @@
     private ActivityManagerService mAms;
     private BroadcastQueue mQueue;
     BroadcastConstants mConstants;
-    private TestBroadcastSkipPolicy mSkipPolicy;
+    private BroadcastSkipPolicy mSkipPolicy;
 
     /**
      * Desired behavior of the next
@@ -287,7 +286,10 @@
         mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
         mConstants.TIMEOUT = 100;
         mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
-        mSkipPolicy = new TestBroadcastSkipPolicy(mAms);
+
+        mSkipPolicy = spy(new BroadcastSkipPolicy(mAms));
+        doReturn(null).when(mSkipPolicy).shouldSkipMessage(any(), any());
+        doReturn(false).when(mSkipPolicy).disallowBackgroundStart(any());
 
         final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
             public void addBroadcastToHistoryLocked(BroadcastRecord original) {
@@ -324,48 +326,6 @@
         }
     }
 
-    private static class TestBroadcastSkipPolicy extends BroadcastSkipPolicy {
-        private final ArrayMap<String, ArraySet> mReceiversToSkip = new ArrayMap<>();
-
-        TestBroadcastSkipPolicy(ActivityManagerService service) {
-            super(service);
-        }
-
-        public String shouldSkipMessage(BroadcastRecord r, Object o) {
-            if (shouldSkipReceiver(r.intent.getAction(), o)) {
-                return "test skipped receiver";
-            }
-            return null;
-        }
-
-        private boolean shouldSkipReceiver(String action, Object o) {
-            final ArraySet<Object> receiversToSkip = mReceiversToSkip.get(action);
-            if (receiversToSkip == null) {
-                return false;
-            }
-            for (int i = 0; i < receiversToSkip.size(); ++i) {
-                if (BroadcastRecord.isReceiverEquals(o, receiversToSkip.valueAt(i))) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        public void setSkipReceiver(String action, Object o) {
-            ArraySet<Object> receiversToSkip = mReceiversToSkip.get(action);
-            if (receiversToSkip == null) {
-                receiversToSkip = new ArraySet<>();
-                mReceiversToSkip.put(action, receiversToSkip);
-            }
-            receiversToSkip.add(o);
-        }
-        public boolean disallowBackgroundStart(BroadcastRecord r) {
-            // Ignored
-            return false;
-        }
-
-    }
-
     private class TestInjector extends Injector {
         TestInjector(Context context) {
             super(context);
@@ -2039,8 +1999,16 @@
             enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
                     List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));
 
-            mSkipPolicy.setSkipReceiver(airplane.getAction(), greenReceiver);
-            mSkipPolicy.setSkipReceiver(airplane.getAction(), orangeReceiver);
+            doAnswer(invocation -> {
+                final BroadcastRecord r = invocation.getArgument(0);
+                final Object o = invocation.getArgument(1);
+                if (airplane.getAction().equals(r.intent.getAction())
+                        && (isReceiverEquals(o, greenReceiver)
+                                || isReceiverEquals(o, orangeReceiver))) {
+                    return "test skipped receiver";
+                }
+                return null;
+            }).when(mSkipPolicy).shouldSkipMessage(any(BroadcastRecord.class), any());
         }
 
         waitForIdle();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index ef8a49f..6431e88 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -25,6 +25,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.verify;
 
+import android.companion.virtual.sensor.IVirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorEvent;
 import android.hardware.Sensor;
@@ -54,6 +55,8 @@
 
     @Mock
     private SensorManagerInternal mSensorManagerInternalMock;
+    @Mock
+    private IVirtualSensorCallback mVirtualSensorCallback;
     private SensorController mSensorController;
     private VirtualSensorEvent mSensorEvent;
     private VirtualSensorConfig mVirtualSensorConfig;
@@ -66,7 +69,8 @@
         LocalServices.removeServiceForTest(SensorManagerInternal.class);
         LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock);
 
-        mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID);
+        mSensorController =
+                new SensorController(new Object(), VIRTUAL_DEVICE_ID, mVirtualSensorCallback);
         mSensorEvent = new VirtualSensorEvent.Builder(new float[] { 1f, 2f, 3f}).build();
         mVirtualSensorConfig =
                 new VirtualSensorConfig.Builder(Sensor.TYPE_ACCELEROMETER, VIRTUAL_SENSOR_NAME)
@@ -135,6 +139,7 @@
     private void doCreateSensorSuccessfully() {
         doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor(
                 anyInt(), anyInt(), anyString(), anyString(), any());
-        mSensorController.createSensor(mSensorToken, mVirtualSensorConfig);
+        assertThat(mSensorController.createSensor(mSensorToken, mVirtualSensorConfig))
+                .isEqualTo(SENSOR_HANDLE);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index ce5ddb0..0cd50f0 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -55,6 +55,9 @@
 import android.companion.virtual.VirtualDeviceParams;
 import android.companion.virtual.audio.IAudioConfigChangedCallback;
 import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.companion.virtual.sensor.IVirtualSensorCallback;
+import android.companion.virtual.sensor.VirtualSensor;
+import android.companion.virtual.sensor.VirtualSensorCallback;
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.content.ComponentName;
 import android.content.Context;
@@ -102,6 +105,7 @@
 
 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
 import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.internal.os.BackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.input.InputManagerInternal;
 import com.android.server.sensors.SensorManagerInternal;
@@ -225,6 +229,10 @@
     @Mock
     private SensorManagerInternal mSensorManagerInternalMock;
     @Mock
+    private IVirtualSensorCallback mVirtualSensorCallback;
+    @Mock
+    private VirtualSensorCallback mSensorCallback;
+    @Mock
     private IVirtualDeviceActivityListener mActivityListener;
     @Mock
     private IVirtualDeviceSoundEffectListener mSoundEffectListener;
@@ -333,7 +341,8 @@
         mInputController = new InputController(new Object(), mNativeWrapperMock,
                 new Handler(TestableLooper.get(this).getLooper()),
                 mContext.getSystemService(WindowManager.class), threadVerifier);
-        mSensorController = new SensorController(new Object(), VIRTUAL_DEVICE_ID_1);
+        mSensorController =
+                new SensorController(new Object(), VIRTUAL_DEVICE_ID_1, mVirtualSensorCallback);
         mCameraAccessController =
                 new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
 
@@ -451,6 +460,42 @@
     }
 
     @Test
+    public void getVirtualSensor_defaultDeviceId_returnsNull() {
+        assertThat(mLocalService.getVirtualSensor(DEVICE_ID_DEFAULT, SENSOR_HANDLE)).isNull();
+    }
+
+    @Test
+    public void getVirtualSensor_invalidDeviceId_returnsNull() {
+        assertThat(mLocalService.getVirtualSensor(DEVICE_ID_INVALID, SENSOR_HANDLE)).isNull();
+    }
+
+    @Test
+    public void getVirtualSensor_noSensors_returnsNull() {
+        assertThat(mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE)).isNull();
+    }
+
+    @Test
+    public void getVirtualSensor_returnsCorrectSensor() {
+        VirtualDeviceParams params = new VirtualDeviceParams.Builder()
+                .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
+                .addVirtualSensorConfig(
+                        new VirtualSensorConfig.Builder(Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1)
+                                .build())
+                .setVirtualSensorCallback(BackgroundThread.getExecutor(), mSensorCallback)
+                .build();
+
+        doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor(
+                anyInt(), anyInt(), anyString(), anyString(), any());
+        mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params);
+
+        VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE);
+        assertThat(sensor).isNotNull();
+        assertThat(sensor.getDeviceId()).isEqualTo(VIRTUAL_DEVICE_ID_1);
+        assertThat(sensor.getHandle()).isEqualTo(SENSOR_HANDLE);
+        assertThat(sensor.getType()).isEqualTo(Sensor.TYPE_ACCELEROMETER);
+    }
+
+    @Test
     public void getDeviceIdsForUid_noRunningApps_returnsNull() {
         Set<Integer> deviceIds = mLocalService.getDeviceIdsForUid(UID_1);
         assertThat(deviceIds).isEmpty();
@@ -888,18 +933,6 @@
     }
 
     @Test
-    public void createVirtualSensor_noPermission_failsSecurityException() {
-        try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
-            assertThrows(
-                    SecurityException.class,
-                    () -> mDeviceImpl.createVirtualSensor(
-                            BINDER,
-                            new VirtualSensorConfig.Builder(
-                                    Sensor.TYPE_ACCELEROMETER, DEVICE_NAME_1).build()));
-        }
-    }
-
-    @Test
     public void onAudioSessionStarting_noPermission_failsSecurityException() {
         mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID_1);
         try (DropShellPermissionsTemporarily drop = new DropShellPermissionsTemporarily()) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
deleted file mode 100644
index 798650d..0000000
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceParamsTest.java
+++ /dev/null
@@ -1,85 +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.companion.virtual;
-
-import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
-import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
-import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
-import static android.hardware.Sensor.TYPE_ACCELEROMETER;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.companion.virtual.VirtualDeviceParams;
-import android.companion.virtual.sensor.VirtualSensorConfig;
-import android.os.Parcel;
-import android.os.UserHandle;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-import java.util.Set;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class VirtualDeviceParamsTest {
-
-    private static final String SENSOR_NAME = "VirtualSensorName";
-    private static final String SENSOR_VENDOR = "VirtualSensorVendor";
-    private static final int PLAYBACK_SESSION_ID = 42;
-    private static final int RECORDING_SESSION_ID = 77;
-
-    @Test
-    public void parcelable_shouldRecreateSuccessfully() {
-        VirtualDeviceParams originalParams = new VirtualDeviceParams.Builder()
-                .setLockState(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED)
-                .setUsersWithMatchingAccounts(Set.of(UserHandle.of(123), UserHandle.of(456)))
-                .setDevicePolicy(POLICY_TYPE_SENSORS, DEVICE_POLICY_CUSTOM)
-                .setDevicePolicy(POLICY_TYPE_AUDIO, DEVICE_POLICY_CUSTOM)
-                .setAudioPlaybackSessionId(PLAYBACK_SESSION_ID)
-                .setAudioRecordingSessionId(RECORDING_SESSION_ID)
-                .addVirtualSensorConfig(
-                        new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME)
-                                .setVendor(SENSOR_VENDOR)
-                                .build())
-                .build();
-        Parcel parcel = Parcel.obtain();
-        originalParams.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-
-        VirtualDeviceParams params = VirtualDeviceParams.CREATOR.createFromParcel(parcel);
-        assertThat(params).isEqualTo(originalParams);
-        assertThat(params.getLockState()).isEqualTo(VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED);
-        assertThat(params.getUsersWithMatchingAccounts())
-                .containsExactly(UserHandle.of(123), UserHandle.of(456));
-        assertThat(params.getDevicePolicy(POLICY_TYPE_SENSORS)).isEqualTo(DEVICE_POLICY_CUSTOM);
-        assertThat(params.getDevicePolicy(POLICY_TYPE_AUDIO)).isEqualTo(DEVICE_POLICY_CUSTOM);
-        assertThat(params.getAudioPlaybackSessionId()).isEqualTo(PLAYBACK_SESSION_ID);
-        assertThat(params.getAudioRecordingSessionId()).isEqualTo(RECORDING_SESSION_ID);
-
-        List<VirtualSensorConfig> sensorConfigs = params.getVirtualSensorConfigs();
-        assertThat(sensorConfigs).hasSize(1);
-        VirtualSensorConfig sensorConfig = sensorConfigs.get(0);
-        assertThat(sensorConfig.getType()).isEqualTo(TYPE_ACCELEROMETER);
-        assertThat(sensorConfig.getName()).isEqualTo(SENSOR_NAME);
-        assertThat(sensorConfig.getVendor()).isEqualTo(SENSOR_VENDOR);
-        assertThat(sensorConfig.getStateChangeCallback()).isNull();
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
index 07a5303..a387d4a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserRestrictionsUtilsTest.java
@@ -20,7 +20,6 @@
 import static com.android.server.devicepolicy.DpmTestUtils.newRestrictions;
 
 import android.os.Bundle;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.test.AndroidTestCase;
@@ -77,30 +76,30 @@
         assertTrue(UserRestrictionsUtils.canDeviceOwnerChange(UserManager.DISALLOW_USER_SWITCH));
     }
 
-    public void testCanProfileOwnerChange() {
-        int user = UserHandle.USER_SYSTEM;
+    public void testCanProfileOwnerChange_mainUser() {
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_RECORD_AUDIO, user));
+                UserManager.DISALLOW_RECORD_AUDIO, true));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_WALLPAPER, user));
+                UserManager.DISALLOW_WALLPAPER, true));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_USER_SWITCH, user));
+                UserManager.DISALLOW_USER_SWITCH, true));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_ADD_USER, user));
+                UserManager.DISALLOW_ADD_USER, true));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_ADJUST_VOLUME, user));
+                UserManager.DISALLOW_ADJUST_VOLUME, true));
+    }
 
-        user = 10;
+    public void testCanProfileOwnerChange_notMainUser() {
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_RECORD_AUDIO, user));
+                UserManager.DISALLOW_RECORD_AUDIO, false));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_WALLPAPER, user));
+                UserManager.DISALLOW_WALLPAPER, false));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_ADD_USER, user));
+                UserManager.DISALLOW_ADD_USER, false));
         assertFalse(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_USER_SWITCH, user));
+                UserManager.DISALLOW_USER_SWITCH, false));
         assertTrue(UserRestrictionsUtils.canProfileOwnerChange(
-                UserManager.DISALLOW_ADJUST_VOLUME, user));
+                UserManager.DISALLOW_ADJUST_VOLUME, false));
     }
 
     public void testMoveRestriction() {
diff --git a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
index 6553ea9..454d3f3 100644
--- a/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/LowPowerStandbyControllerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.power;
 
+import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST;
 import static android.os.PowerManager.LOW_POWER_STANDBY_ALLOWED_REASON_VOICE_INTERACTION;
 import static android.os.PowerManager.LOW_POWER_STANDBY_FEATURE_WAKE_ON_LAN;
 
@@ -62,6 +63,8 @@
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
+import com.android.server.PowerAllowlistInternal;
+import com.android.server.PowerAllowlistInternal.TempAllowlistChangeListener;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.power.LowPowerStandbyController.DeviceConfigWrapper;
 import com.android.server.testutils.OffsettableClock;
@@ -117,6 +120,8 @@
     private PowerManagerInternal mPowerManagerInternalMock;
     @Mock
     private NetworkPolicyManagerInternal mNetworkPolicyManagerInternalMock;
+    @Mock
+    private PowerAllowlistInternal mPowerAllowlistInternalMock;
 
     @Before
     public void setUp() throws Exception {
@@ -130,6 +135,7 @@
         when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(powerManager);
         addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock);
         addLocalServiceMock(NetworkPolicyManagerInternal.class, mNetworkPolicyManagerInternalMock);
+        addLocalServiceMock(PowerAllowlistInternal.class, mPowerAllowlistInternalMock);
 
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
 
@@ -169,6 +175,7 @@
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
         LocalServices.removeServiceForTest(LowPowerStandbyControllerInternal.class);
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerAllowlistInternal.class);
         mTestPolicyFile.delete();
     }
 
@@ -614,7 +621,7 @@
 
         InOrder inOrder = inOrder(mPowerManagerInternalMock);
 
-        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
+        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{
                 UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID),
                 UserHandle.getUid(USER_ID_2, TEST_PKG1_APP_ID),
         });
@@ -626,7 +633,7 @@
         mContextSpy.sendBroadcast(intent);
         mTestLooper.dispatchAll();
 
-        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[] {
+        inOrder.verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{
                 UserHandle.getUid(USER_ID_1, TEST_PKG1_APP_ID)
         });
         inOrder.verifyNoMoreInteractions();
@@ -663,6 +670,39 @@
         assertFalse(mController.isPackageExempt(TEST_PKG1_APP_ID));
     }
 
+    @Test
+    public void testAllowReason_tempPowerSaveAllowlist() throws Exception {
+        mController.systemReady();
+        mController.setEnabled(true);
+        mController.setPolicy(policyWithAllowedReasons(
+                LOW_POWER_STANDBY_ALLOWED_REASON_TEMP_POWER_SAVE_ALLOWLIST));
+        mTestLooper.dispatchAll();
+
+        ArgumentCaptor<TempAllowlistChangeListener> tempAllowlistChangeListenerArgumentCaptor =
+                ArgumentCaptor.forClass(TempAllowlistChangeListener.class);
+        verify(mPowerAllowlistInternalMock).registerTempAllowlistChangeListener(
+                tempAllowlistChangeListenerArgumentCaptor.capture());
+        TempAllowlistChangeListener tempAllowlistChangeListener =
+                tempAllowlistChangeListenerArgumentCaptor.getValue();
+
+        tempAllowlistChangeListener.onAppAdded(TEST_PKG1_APP_ID);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{TEST_PKG1_APP_ID});
+
+        tempAllowlistChangeListener.onAppAdded(TEST_PKG2_APP_ID);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(
+                new int[]{TEST_PKG1_APP_ID, TEST_PKG2_APP_ID});
+
+        tempAllowlistChangeListener.onAppRemoved(TEST_PKG1_APP_ID);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[]{TEST_PKG2_APP_ID});
+
+        mController.setPolicy(EMPTY_POLICY);
+        mTestLooper.dispatchAll();
+        verify(mPowerManagerInternalMock).setLowPowerStandbyAllowlist(new int[0]);
+    }
+
     private void setInteractive() throws Exception {
         when(mIPowerManagerMock.isInteractive()).thenReturn(true);
         mContextSpy.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 9c68ddc..33ca5c2 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -302,30 +302,6 @@
     }
 
     @Test
-    public void testXmlMigratingAllowedAdjustments() throws Exception {
-        // Old tag, need migration
-        String xml = "<q_allowed_adjustments types=\"adj_1\"/>";
-
-        TypedXmlPullParser parser = Xml.newFastPullParser();
-        parser.setInput(new BufferedInputStream(
-                new ByteArrayInputStream(xml.toString().getBytes())), null);
-        parser.nextTag();
-        mAssistants.readExtraTag("q_allowed_adjustments", parser);
-        assertTrue(mAssistants.isAdjustmentAllowed("adj_1"));
-        assertEquals(mNm.DEFAULT_ALLOWED_ADJUSTMENTS.length + 1,
-                mAssistants.getAllowedAssistantAdjustments().size());
-
-        // New TAG
-        xml = "<s_allowed_adjustments types=\"adj_2\"/>";
-        parser.setInput(new BufferedInputStream(
-                new ByteArrayInputStream(xml.toString().getBytes())), null);
-        parser.nextTag();
-        mAssistants.readExtraTag("s_allowed_adjustments", parser);
-        assertTrue(mAssistants.isAdjustmentAllowed("adj_2"));
-        assertEquals(1, mAssistants.getAllowedAssistantAdjustments().size());
-    }
-
-    @Test
     public void testSetPackageOrComponentEnabled_onlyOnePackage() throws Exception {
         ComponentName component1 = ComponentName.unflattenFromString("package/Component1");
         ComponentName component2 = ComponentName.unflattenFromString("package/Component2");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 69fccf2..009b259 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -203,7 +203,6 @@
 
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
-import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
 import com.android.internal.logging.InstanceIdSequence;
 import com.android.internal.logging.InstanceIdSequenceFake;
 import com.android.internal.messages.nano.SystemMessageProto;
@@ -7619,17 +7618,8 @@
 
     @Test
     public void testGetAllowedAssistantAdjustments() throws Exception {
-        List<String> capabilities = mBinderService.getAllowedAssistantAdjustments(null);
-        assertNotNull(capabilities);
-
-        for (int i = capabilities.size() - 1; i >= 0; i--) {
-            String capability = capabilities.get(i);
-            mBinderService.disallowAssistantAdjustment(capability);
-            assertEquals(i + 1, mBinderService.getAllowedAssistantAdjustments(null).size());
-            List<String> currentCapabilities = mBinderService.getAllowedAssistantAdjustments(null);
-            assertNotNull(currentCapabilities);
-            assertFalse(currentCapabilities.contains(capability));
-        }
+        List<String> adjustments = mBinderService.getAllowedAssistantAdjustments(null);
+        assertNotNull(adjustments);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityInterceptorCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityInterceptorCallbackTest.java
new file mode 100644
index 0000000..ff0591b
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityInterceptorCallbackTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 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.google.common.truth.Truth.assertThat;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ResolveInfo;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+
+import org.junit.Test;
+
+@Presubmit
+@MediumTest
+public class ActivityInterceptorCallbackTest {
+
+    @Test
+    public void testBuildActivityInterceptorCallback() {
+        int callingUid = 10;
+        int callingPid = 100;
+        int realCallingUid = 20;
+        int realCallingPid = 200;
+        int userId = 1;
+        Intent intent = new Intent();
+        ResolveInfo resolveInfo = new ResolveInfo();
+        ActivityInfo activityInfo = new ActivityInfo();
+        String resolveType = "resolveType";
+        String callingPackage = "callingPackage";
+        String callingFeatureId = "callingFeatureId";
+        ActivityOptions activityOptions = ActivityOptions.makeBasic();
+        Runnable clearOptionsAnimation = () -> {};
+
+        ActivityInterceptorCallback.ActivityInterceptorInfo activityInterceptorInfo =
+                new ActivityInterceptorCallback.ActivityInterceptorInfo.Builder(callingUid,
+                        callingPid, realCallingUid, realCallingPid, userId, intent, resolveInfo,
+                        activityInfo)
+                .setResolvedType(resolveType)
+                .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
+                .setCheckedOptions(activityOptions)
+                .setClearOptionsAnimationRunnable(clearOptionsAnimation)
+                .build();
+
+        assertThat(activityInterceptorInfo.getCallingUid()).isEqualTo(callingUid);
+        assertThat(activityInterceptorInfo.getCallingPid()).isEqualTo(callingPid);
+        assertThat(activityInterceptorInfo.getRealCallingUid()).isEqualTo(realCallingUid);
+        assertThat(activityInterceptorInfo.getRealCallingPid()).isEqualTo(realCallingPid);
+        assertThat(activityInterceptorInfo.getUserId()).isEqualTo(userId);
+        assertThat(activityInterceptorInfo.getIntent()).isEqualTo(intent);
+        assertThat(activityInterceptorInfo.getResolveInfo()).isEqualTo(resolveInfo);
+        assertThat(activityInterceptorInfo.getActivityInfo()).isEqualTo(activityInfo);
+        assertThat(activityInterceptorInfo.getResolvedType()).isEqualTo(resolveType);
+        assertThat(activityInterceptorInfo.getCallingPackage()).isEqualTo(callingPackage);
+        assertThat(activityInterceptorInfo.getCallingFeatureId()).isEqualTo(callingFeatureId);
+        assertThat(activityInterceptorInfo.getCheckedOptions()).isEqualTo(activityOptions);
+        assertThat(activityInterceptorInfo.getClearOptionsAnimationRunnable())
+                .isEqualTo(clearOptionsAnimation);
+    }
+
+    @Test
+    public void testActivityInterceptResult() {
+        Intent intent = new Intent();
+        ActivityOptions activityOptions = ActivityOptions.makeBasic();
+        boolean isActivityResolved = true;
+
+        ActivityInterceptorCallback.ActivityInterceptResult result =
+                new ActivityInterceptorCallback.ActivityInterceptResult(intent, activityOptions);
+        assertThat(result.getIntent()).isEqualTo(intent);
+        assertThat(result.getActivityOptions()).isEqualTo(activityOptions);
+        assertThat(result.isActivityResolved()).isFalse();
+
+        result = new ActivityInterceptorCallback.ActivityInterceptResult(
+                intent, activityOptions, isActivityResolved);
+        assertThat(result.getIntent()).isEqualTo(intent);
+        assertThat(result.getActivityOptions()).isEqualTo(activityOptions);
+        assertThat(result.isActivityResolved()).isTrue();
+    }
+}
diff --git a/telecomm/java/android/telecom/CallControl.java b/telecomm/java/android/telecom/CallControl.java
index f3c91f6..2135e27 100644
--- a/telecomm/java/android/telecom/CallControl.java
+++ b/telecomm/java/android/telecom/CallControl.java
@@ -197,10 +197,10 @@
      *                 of the requested operation.
      *
      *                 {@link OutcomeReceiver#onResult} will be called if Telecom has successfully
-     *                 rejected the incoming call.
+     *                 started the call streaming.
      *
      *                 {@link OutcomeReceiver#onError} will be called if Telecom has failed to
-     *                 reject the incoming call.  A {@link CallException} will be passed that
+     *                 start the call streaming. A {@link CallException} will be passed that
      *                 details why the operation failed.
      */
     public void startCallStreaming(@CallbackExecutor @NonNull Executor executor,
diff --git a/telecomm/java/android/telecom/CallStreamingService.java b/telecomm/java/android/telecom/CallStreamingService.java
index 7f11128..3f538a7 100644
--- a/telecomm/java/android/telecom/CallStreamingService.java
+++ b/telecomm/java/android/telecom/CallStreamingService.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Handler;
@@ -38,10 +39,22 @@
 /**
  * This service is implemented by an app that wishes to provide functionality for a general call
  * streaming sender for voip calls.
- *
- * TODO: add doc of how to be the general streaming sender
- *
+ * <p>
+ * Below is an example manifest registration for a {@code CallStreamingService}.
+ * <pre>
+ * {@code
+ * <service android:name=".EgCallStreamingService"
+ *     android:permission="android.permission.BIND_CALL_STREAMING_SERVICE" >
+ *     ...
+ *     <intent-filter>
+ *         <action android:name="android.telecom.CallStreamingService" />
+ *     </intent-filter>
+ * </service>
+ * }
+ * </pre>
+ * @hide
  */
+@SystemApi
 public abstract class CallStreamingService extends Service {
     /**
      * The {@link android.content.Intent} that must be declared as handled by the service.
@@ -122,6 +135,13 @@
     /**
      * Call streaming request reject reason used with
      * {@link CallEventCallback#onCallStreamingFailed(int)} to indicate that telecom is rejecting a
+     * call streaming request due to unknown reason.
+     */
+    public static final int STREAMING_FAILED_UNKNOWN = 0;
+
+    /**
+     * Call streaming request reject reason used with
+     * {@link CallEventCallback#onCallStreamingFailed(int)} to indicate that telecom is rejecting a
      * call streaming request because there's an ongoing streaming call on this device.
      */
     public static final int STREAMING_FAILED_ALREADY_STREAMING = 1;
@@ -149,6 +169,7 @@
      */
     @IntDef(prefix = {"STREAMING_FAILED"},
             value = {
+                    STREAMING_FAILED_UNKNOWN,
                     STREAMING_FAILED_ALREADY_STREAMING,
                     STREAMING_FAILED_NO_SENDER,
                     STREAMING_FAILED_SENDER_BINDING_ERROR
diff --git a/telecomm/java/android/telecom/StreamingCall.java b/telecomm/java/android/telecom/StreamingCall.java
index 4b27dd6..d4f4322 100644
--- a/telecomm/java/android/telecom/StreamingCall.java
+++ b/telecomm/java/android/telecom/StreamingCall.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.os.Bundle;
@@ -30,7 +31,10 @@
 /**
  * Represents a voip call requested to stream to another device that the general streaming sender
  * app should present to the receiver.
+ *
+ * @hide
  */
+@SystemApi
 public final class StreamingCall implements Parcelable {
     /**
      * The state of a {@code StreamingCall} when newly created. General streaming sender should
@@ -50,9 +54,12 @@
      */
     public static final int STATE_DISCONNECTED = 3;
 
+    /**
+     * @hide
+     */
     private StreamingCall(@NonNull Parcel in) {
         mComponentName = in.readParcelable(ComponentName.class.getClassLoader());
-        mDisplayName = in.readString16NoHelper();
+        mDisplayName = in.readCharSequence();
         mAddress = in.readParcelable(Uri.class.getClassLoader());
         mExtras = in.readBundle();
         mState = in.readInt();
@@ -79,7 +86,7 @@
     @Override
     public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) {
         dest.writeParcelable(mComponentName, flags);
-        dest.writeString16NoHelper(mDisplayName);
+        dest.writeCharSequence(mDisplayName);
         dest.writeParcelable(mAddress, flags);
         dest.writeBundle(mExtras);
         dest.writeInt(mState);
@@ -98,14 +105,14 @@
     public @interface StreamingCallState {}
 
     private final ComponentName mComponentName;
-    private final String mDisplayName;
+    private final CharSequence mDisplayName;
     private final Uri mAddress;
     private final Bundle mExtras;
     @StreamingCallState
     private int mState;
     private StreamingCallAdapter mAdapter = null;
 
-    public StreamingCall(@NonNull ComponentName componentName, @NonNull String displayName,
+    public StreamingCall(@NonNull ComponentName componentName, @NonNull CharSequence displayName,
             @NonNull Uri address, @NonNull Bundle extras) {
         mComponentName = componentName;
         mDisplayName = displayName;
@@ -137,7 +144,7 @@
      * {@code StreamingCall} to the receiver side.
      */
     @NonNull
-    public String getDisplayName() {
+    public CharSequence getDisplayName() {
         return mDisplayName;
     }
 
diff --git a/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesConsumer.aidl b/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesConsumer.aidl
deleted file mode 100644
index f3ae245..0000000
--- a/telephony/java/android/telephony/satellite/ISatelliteCapabilitiesConsumer.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2023 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.telephony.satellite;
-
-import android.telephony.satellite.SatelliteCapabilities;
-
-/**
- * Consumer for a SatelliteCapabilities result from the satellite service.
- * @hide
- */
-oneway interface ISatelliteCapabilitiesConsumer {
-    void accept(in SatelliteCapabilities result);
-}
diff --git a/telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl b/telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl
index 6fb0979..0ce9c98 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteStateListener.aidl
@@ -17,13 +17,17 @@
 package android.telephony.satellite;
 
 import android.telephony.satellite.PointingInfo;
+import android.telephony.satellite.SatelliteDatagram;
 
 /**
  * Interface for satellite state listener.
  * @hide
  */
 oneway interface ISatelliteStateListener {
-    void onSatelliteProvisionStateChanged(in int[] features, in boolean provisioned);
+    void onSatelliteProvisionStateChanged(in boolean provisioned);
     void onSatellitePositionUpdate(in PointingInfo pointingInfo);
     void onMessageTransferStateUpdate(in int state);
+    void onSatelliteModemStateChange(in int state);
+    void onPendingMessageCount(in int count);
+    void onSatelliteDatagrams(in SatelliteDatagram[] datagrams);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteCallback.java b/telephony/java/android/telephony/satellite/SatelliteCallback.java
index 1b82d06..5250562 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCallback.java
@@ -18,9 +18,9 @@
 
 import android.annotation.NonNull;
 import android.os.Binder;
-import android.telephony.satellite.stub.SatelliteImplBase;
 
 import java.lang.ref.WeakReference;
+import java.util.List;
 import java.util.concurrent.Executor;
 
 /**
@@ -63,12 +63,10 @@
         /**
          * Called when satellite provision state changes.
          *
-         * @param features The list of provisioned features.
          * @param provisioned The new provision state. {@code true} means satellite is provisioned
          *                    {@code false} means satellite is not provisioned.
          */
-        void onSatelliteProvisionStateChanged(
-                @SatelliteImplBase.Feature int[] features, boolean provisioned);
+        void onSatelliteProvisionStateChanged(boolean provisioned);
     }
 
     /**
@@ -91,6 +89,34 @@
                 @SatelliteManager.SatelliteMessageTransferState int state);
     }
 
+    /**
+     * Interface for satellite state change listener.
+     */
+    public interface SatelliteStateListener {
+        /**
+         * Called when satellite state changes.
+         * @param state - The new satellite state.
+         */
+        void onSatelliteModemStateChange(@SatelliteManager.SatelliteModemState int state);
+
+        /**
+         * Called when there are pending messages to be received from satellite.
+         * @param count - pending message count.
+         */
+        void onPendingMessageCount(int count);
+    }
+
+    /**
+     * Interface for satellite datagram listener.
+     */
+    public interface SatelliteDatagramListener {
+        /**
+         * Called when there are incoming datagrams to be received.
+         * @param datagrams - datagrams to be received over satellite.
+         */
+        void onSatelliteDatagrams(SatelliteDatagram[] datagrams);
+    }
+
     private static class ISatelliteStateListenerStub extends ISatelliteStateListener.Stub {
         private WeakReference<SatelliteCallback> mSatelliteCallbackWeakRef;
         private Executor mExecutor;
@@ -100,14 +126,13 @@
             mExecutor = executor;
         }
 
-        public void onSatelliteProvisionStateChanged(
-                @SatelliteImplBase.Feature int[] features, boolean provisioned) {
+        public void onSatelliteProvisionStateChanged(boolean provisioned) {
             SatelliteProvisionStateListener listener =
                     (SatelliteProvisionStateListener) mSatelliteCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(() -> mExecutor.execute(
-                    () -> listener.onSatelliteProvisionStateChanged(features, provisioned)));
+                    () -> listener.onSatelliteProvisionStateChanged(provisioned)));
         }
 
         public void onSatellitePositionUpdate(@NonNull PointingInfo pointingInfo) {
@@ -128,5 +153,36 @@
             Binder.withCleanCallingIdentity(() -> mExecutor.execute(
                     () -> listener.onMessageTransferStateUpdate(state)));
         }
+
+
+        @Override
+        public void onSatelliteModemStateChange(@SatelliteManager.SatelliteModemState int state) {
+            SatelliteStateListener listener =
+                    (SatelliteStateListener) mSatelliteCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onSatelliteModemStateChange(state)));
+        }
+
+        @Override
+        public void onPendingMessageCount(int count) {
+            SatelliteStateListener listener =
+                    (SatelliteStateListener) mSatelliteCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onPendingMessageCount(count)));
+        }
+
+        @Override
+        public void onSatelliteDatagrams(SatelliteDatagram[] datagrams) {
+            SatelliteDatagramListener listener =
+                    (SatelliteDatagramListener) mSatelliteCallbackWeakRef.get();
+            if (listener == null) return;
+
+            Binder.withCleanCallingIdentity(() -> mExecutor.execute(
+                    () -> listener.onSatelliteDatagrams(datagrams)));
+        }
     }
 }
diff --git a/core/java/android/nfc/BeamShareData.aidl b/telephony/java/android/telephony/satellite/SatelliteDatagram.aidl
similarity index 74%
copy from core/java/android/nfc/BeamShareData.aidl
copy to telephony/java/android/telephony/satellite/SatelliteDatagram.aidl
index a47e240..993aacf 100644
--- a/core/java/android/nfc/BeamShareData.aidl
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright 2023, 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
+ *     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,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.nfc;
+package android.telephony.satellite;
 
-parcelable BeamShareData;
+parcelable SatelliteDatagram;
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagram.java b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
new file mode 100644
index 0000000..cc5a9f4
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public final class SatelliteDatagram implements Parcelable {
+    /**
+     * Datagram to be sent or received over satellite.
+     */
+    private byte[] mData;
+
+    /**
+     * @hide
+     */
+    public SatelliteDatagram(@NonNull byte[] data) {
+        mData = data;
+    }
+
+    private SatelliteDatagram(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeByteArray(mData);
+    }
+
+    public static final @android.annotation.NonNull Creator<SatelliteDatagram> CREATOR =
+            new Creator<SatelliteDatagram>() {
+                @Override
+                public SatelliteDatagram createFromParcel(Parcel in) {
+                    return new SatelliteDatagram(in);
+                }
+
+                @Override
+                public SatelliteDatagram[] newArray(int size) {
+                    return new SatelliteDatagram[size];
+                }
+            };
+
+    @Nullable
+    public byte[] getSatelliteDatagram() {
+        return mData;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mData = in.createByteArray();
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index a818b63..5f6218d 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -26,15 +26,15 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyFrameworkInitializer;
-import android.telephony.satellite.stub.SatelliteImplBase;
 
-import com.android.internal.telephony.IBooleanConsumer;
-import com.android.internal.telephony.IIntArrayConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.ITelephony;
 import com.android.telephony.Rlog;
@@ -72,16 +72,6 @@
     }
 
     /**
-     * Create a new SatelliteManager associated with the given subscription ID.
-     *
-     * @param subId The subscription ID to create the SatelliteManager with.
-     * @return A SatelliteManager that uses the given subscription ID for all calls.
-     */
-    @NonNull public SatelliteManager createForSubscriptionId(int subId) {
-        return new SatelliteManager(mContext, subId);
-    }
-
-    /**
      * Create an instance of the SatelliteManager associated with a particular subscription.
      *
      * @param context The context the SatelliteManager belongs to.
@@ -93,6 +83,67 @@
     }
 
     /**
+     * Exception from the satellite service containing the {@link SatelliteError} error code.
+     */
+    public static class SatelliteException extends Exception {
+        @SatelliteError private final int mErrorCode;
+
+        /**
+         * Create a SatelliteException with a given error code.
+         *
+         * @param errorCode The {@link SatelliteError}.
+         */
+        public SatelliteException(@SatelliteError int errorCode) {
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * Get the error code returned from the satellite service.
+         *
+         * @return The {@link SatelliteError}.
+         */
+        @SatelliteError public int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_ENABLED = "satellite_enabled";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestMaxCharactersPerSatelliteTextMessage(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT =
+            "max_characters_per_satellite_text";
+
+    /**
+     * Bundle key to get the response from
+     * {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}.
+     * @hide
+     */
+    public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned";
+
+    /**
      * The request was successfully processed.
      */
     public static final int SATELLITE_ERROR_NONE = 0;
@@ -133,8 +184,8 @@
      */
     public static final int SATELLITE_INVALID_ARGUMENTS = 8;
     /**
-     * Telephony framework failed to send a request to the vendor service or the satellite
-     * modem due to internal error.
+     * Telephony framework failed to send a request or receive a response from the vendor service
+     * or satellite modem due to internal error.
      */
     public static final int SATELLITE_REQUEST_FAILED = 9;
     /**
@@ -211,158 +262,201 @@
     public @interface SatelliteError {}
 
     /**
-     * Power on or off the satellite modem.
+     * Enable or disable the satellite modem. If the satellite modem is enabled, this will also
+     * disable the cellular modem, and if the satellite modem is disabled, this will also re-enable
+     * the cellular modem.
      *
-     * @param powerOn {@code true} to power on the satellite modem and {@code false} to power off.
+     * @param enable {@code true} to enable the satellite modem and {@code false} to disable.
+     * @param executor The executor on which the error code listener will be called.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError public int setSatellitePower(boolean powerOn) {
+    public void setSatelliteEnabled(boolean enable, @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> errorCodeListener) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
+
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.setSatellitePower(mSubId, powerOn);
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
+                    }
+                };
+                telephony.setSatelliteEnabled(mSubId, enable, errorCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "setSatellitePower RemoteException", ex);
+            Rlog.e(TAG, "setSatelliteEnabled RemoteException: ", ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
-     * Check whether the satellite modem is powered on.
+     * Request to get whether the satellite modem is enabled.
      *
-     * @param executor The executor on which the result listener will be called.
-     * @param resultListener Listener with the result if the operation is successful.
-     *                       If this method returns {@link #SATELLITE_ERROR_NONE}, the result
-     *                       listener will return {@code true} if the satellite modem is powered on
-     *                       and {@code false} otherwise.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the satellite modem
+     *                 is powered on and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteError}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError public int isSatellitePowerOn(
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<Boolean> resultListener) {
+    public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
-        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                IBooleanConsumer internalCallback = new IBooleanConsumer.Stub() {
+                ResultReceiver receiver = new ResultReceiver(null) {
                     @Override
-                    public void accept(boolean result) {
-                        executor.execute(() -> Binder.withCleanCallingIdentity(
-                                () -> resultListener.accept(result)));
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_ERROR_NONE) {
+                            if (resultData.containsKey(KEY_SATELLITE_ENABLED)) {
+                                boolean isSatelliteEnabled =
+                                        resultData.getBoolean(KEY_SATELLITE_ENABLED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteEnabled)));
+                            } else {
+                                loge("KEY_SATELLITE_ENABLED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(
+                                                new SatelliteException(SATELLITE_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
                     }
                 };
-
-                return telephony.isSatellitePowerOn(mSubId, internalCallback);
+                telephony.requestIsSatelliteEnabled(mSubId, receiver);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("isSatellitePowerOn() RemoteException:" + ex);
+            loge("requestIsSatelliteEnabled() RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
-     * Check whether the satellite service is supported on the device.
+     * Request to get whether the satellite service is supported on the device.
      *
-     * @param executor The executor on which the result listener will be called.
-     * @param resultListener Listener with the result if the operation is successful.
-     *                       If this method returns {@link #SATELLITE_ERROR_NONE}, the result
-     *                       listener will return {@code true} if the satellite service is supported
-     *                       and {@code false} otherwise.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the satellite
+     *                 service is supported on the device and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteError}.
      *
-     * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
-    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError public int isSatelliteSupported(
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<Boolean> resultListener) {
+    public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
-        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                IBooleanConsumer internalCallback = new IBooleanConsumer.Stub() {
+                ResultReceiver receiver = new ResultReceiver(null) {
                     @Override
-                    public void accept(boolean result) {
-                        executor.execute(() -> Binder.withCleanCallingIdentity(
-                                () -> resultListener.accept(result)));
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_ERROR_NONE) {
+                            if (resultData.containsKey(KEY_SATELLITE_SUPPORTED)) {
+                                boolean isSatelliteSupported =
+                                        resultData.getBoolean(KEY_SATELLITE_SUPPORTED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteSupported)));
+                            } else {
+                                loge("KEY_SATELLITE_SUPPORTED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(
+                                                new SatelliteException(SATELLITE_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
                     }
                 };
-
-                return telephony.isSatelliteSupported(mSubId, internalCallback);
+                telephony.requestIsSatelliteSupported(mSubId, receiver);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("isSatelliteSupported() RemoteException:" + ex);
+            loge("requestIsSatelliteSupported() RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
-     * Get the {@link SatelliteCapabilities} with all capabilities of the satellite service.
+     * Request to get the {@link SatelliteCapabilities} of the satellite service.
      *
-     * @param executor The executor on which the result listener will be called.
-     * @param resultListener Listener with the result if the operation is successful.
-     *                       If this method returns {@link #SATELLITE_ERROR_NONE}, the result
-     *                       listener will return the current {@link SatelliteCapabilities}.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return the {@link SatelliteCapabilities} of the satellite service.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteError}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError public int getSatelliteCapabilities(
-            @NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<SatelliteCapabilities> resultListener) {
+    public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
         Objects.requireNonNull(executor);
-        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                ISatelliteCapabilitiesConsumer internalCallback =
-                        new ISatelliteCapabilitiesConsumer.Stub() {
+                ResultReceiver receiver = new ResultReceiver(null) {
                     @Override
-                    public void accept(SatelliteCapabilities result) {
-                        executor.execute(() -> Binder.withCleanCallingIdentity(
-                                () -> resultListener.accept(result)));
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_ERROR_NONE) {
+                            if (resultData.containsKey(KEY_SATELLITE_CAPABILITIES)) {
+                                SatelliteCapabilities capabilities =
+                                        resultData.getParcelable(KEY_SATELLITE_CAPABILITIES,
+                                                SatelliteCapabilities.class);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(capabilities)));
+                            } else {
+                                loge("KEY_SATELLITE_CAPABILITIES does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(
+                                                new SatelliteException(SATELLITE_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
                     }
                 };
-
-                return telephony.getSatelliteCapabilities(mSubId, internalCallback);
+                telephony.requestSatelliteCapabilities(mSubId, receiver);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("getSatelliteCapabilities() RemoteException:" + ex);
+            loge("requestSatelliteCapabilities() RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
@@ -396,6 +490,44 @@
     })
     public @interface SatelliteMessageTransferState {}
 
+    /* Satellite modem is in idle state. */
+    public static final int SATELLITE_MODEM_STATE_IDLE = 0;
+
+    /* Satellite modem is listening for incoming messages. */
+    public static final int SATELLITE_MODEM_STATE_LISTENING = 1;
+
+    /* Satellite modem is sending and/or receiving messages. */
+    public static final int SATELLITE_MODEM_STATE_MESSAGE_TRANSFERRING = 2;
+
+    /* Satellite modem is powered off */
+    public static final int SATELLITE_MODEM_STATE_OFF = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"SATELLITE_STATE_"},
+            value = {
+                    SATELLITE_MODEM_STATE_IDLE,
+                    SATELLITE_MODEM_STATE_LISTENING,
+                    SATELLITE_MODEM_STATE_MESSAGE_TRANSFERRING,
+                    SATELLITE_MODEM_STATE_OFF
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SatelliteModemState {}
+
+    /** SOS SMS */
+    public static final int DATAGRAM_TYPE_SOS_SMS = 0;
+
+    /** Location sharing message */
+    public static final int DATAGRAM_TYPE_LOCATION_SHARING = 3;
+
+    @IntDef(
+            prefix = "DATAGRAM_TYPE_",
+            value = {
+                    DATAGRAM_TYPE_SOS_SMS,
+                    DATAGRAM_TYPE_LOCATION_SHARING,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DatagramType {}
+
     /**
      * Start receiving satellite position updates.
      * This can be called by the pointing UI when the user starts pointing to the satellite.
@@ -403,28 +535,35 @@
      * Satellite position updates are started only on {@link #SATELLITE_ERROR_NONE}.
      * All other results indicate that this operation failed.
      *
-     * @param executor The executor on which the callback will be called.
+     * @param executor The executor on which the callback and error code listener will be called.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
      * @param callback The callback to notify of changes in satellite position. This
      *                 SatelliteCallback should implement the interface
      *                 {@link SatelliteCallback.SatellitePositionUpdateListener}.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError
-    public int startSatellitePositionUpdates(
-            @NonNull Executor executor, @NonNull SatelliteCallback callback) {
+    public void startSatellitePositionUpdates(@NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> errorCodeListener, @NonNull SatelliteCallback callback) {
         Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
         Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 callback.init(executor);
-                return telephony.startSatellitePositionUpdates(mSubId, callback.getCallbackStub());
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
+                    }
+                };
+                telephony.startSatellitePositionUpdates(mSubId, errorCallback,
+                        callback.getCallbackStub());
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -432,7 +571,6 @@
             loge("startSatellitePositionUpdates RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
@@ -441,25 +579,35 @@
      * Satellite position updates are stopped only on {@link #SATELLITE_ERROR_NONE}.
      * All other results indicate that this operation failed.
      *
-     * @param callback The callback that was passed in {@link
-     *                 #startSatellitePositionUpdates(Executor, SatelliteCallback)}.
+     * @param callback The callback that was passed to
+     *       {@link #startSatellitePositionUpdates(Executor, Consumer, SatelliteCallback)}.
+     * @param executor The executor on which the error code listener will be called.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalArgumentException if the callback is invalid.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError
-    public int stopSatellitePositionUpdates(
-            @NonNull SatelliteCallback callback) {
+    public void stopSatellitePositionUpdates(@NonNull SatelliteCallback callback,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> errorCodeListener) {
         Objects.requireNonNull(callback);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.stopSatellitePositionUpdates(mSubId, callback.getCallbackStub());
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
+                    }
+                };
+                telephony.stopSatellitePositionUpdates(mSubId, errorCallback,
+                        callback.getCallbackStub());
                 // TODO: Notify SmsHandler that pointing UI stopped
             } else {
                 throw new IllegalStateException("telephony service is null.");
@@ -468,89 +616,98 @@
             loge("stopSatellitePositionUpdates RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
-     * Get maximum number of characters per text message on satellite.
-     * @param executor - The executor on which the result listener will be called.
-     * @param resultListener - Listener that will be called when the operation is successful.
-     *                       If this method returns {@link #SATELLITE_ERROR_NONE}, listener
-     *                       will be called with maximum characters limit.
+     * Request to get the maximum number of characters per text message on satellite.
+     *
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return the maximum number of characters per text message on satellite.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     *
-     * @return The result of the operation.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError
-    public int getMaxCharactersPerSatelliteTextMessage(@NonNull @CallbackExecutor Executor executor,
-            @NonNull Consumer<Integer> resultListener) {
+    public void requestMaxCharactersPerSatelliteTextMessage(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Integer, SatelliteException> callback) {
         Objects.requireNonNull(executor);
-        Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+                ResultReceiver receiver = new ResultReceiver(null) {
                     @Override
-                    public void accept(int result) {
-                        executor.execute(() -> Binder.withCleanCallingIdentity(
-                                () -> resultListener.accept(result)));
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_ERROR_NONE) {
+                            if (resultData.containsKey(KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT)) {
+                                int maxCharacters =
+                                        resultData.getInt(KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(maxCharacters)));
+                            } else {
+                                loge("KEY_MAX_CHARACTERS_PER_SATELLITE_TEXT does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(
+                                                new SatelliteException(SATELLITE_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
                     }
                 };
-
-                return telephony.getMaxCharactersPerSatelliteTextMessage(mSubId, internalCallback);
+                telephony.requestMaxCharactersPerSatelliteTextMessage(mSubId, receiver);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("getMaxCharactersPerSatelliteTextMessage() RemoteException:" + ex);
+            loge("requestMaxCharactersPerSatelliteTextMessage() RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
 
     /**
-     * Register the subscription with a satellite provider. This is needed if the provider allows
-     * dynamic registration.
+     * Provision the device with a satellite provider.
+     * This is needed if the provider allows dynamic registration.
      *
-     * @param features List of features to be provisioned.
-     * @param executor The optional executor to run callbacks on.
-     * @param callback The optional callback to get the error code of the request.
+     * @param token The token to be used as a unique identifier for provisioning with satellite
+     *              gateway.
      * @param cancellationSignal The optional signal used by the caller to cancel the provision
      *                           request. Even when the cancellation is signaled, Telephony will
      *                           still trigger the callback to return the result of this request.
+     * @param executor The executor on which the error code listener will be called.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
+     *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    public void provisionSatelliteService(
-            @NonNull @SatelliteImplBase.Feature int[] features,
-            @Nullable @CallbackExecutor Executor executor,
-            @SatelliteError @Nullable Consumer<Integer> callback,
-            @Nullable CancellationSignal cancellationSignal) {
-        Objects.requireNonNull(features);
+    public void provisionSatelliteService(@NonNull String token,
+            @Nullable CancellationSignal cancellationSignal,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteError @NonNull Consumer<Integer> errorCodeListener) {
+        Objects.requireNonNull(token);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
 
         ICancellationSignal cancelRemote = null;
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                IIntegerConsumer callbackStub = new IIntegerConsumer.Stub() {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
                     @Override
                     public void accept(int result) {
-                        if (executor == null || callback == null) {
-                            logd("provisionSatelliteService: executor and/or callback is null");
-                            return;
-                        }
-                        Binder.withCleanCallingIdentity(() -> {
-                            executor.execute(() -> callback.accept(result));
-                        });
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
                     }
                 };
-                cancelRemote = telephony.provisionSatelliteService(mSubId, features, callbackStub);
+                cancelRemote = telephony.provisionSatelliteService(mSubId, token, errorCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -564,29 +721,80 @@
     }
 
     /**
-     * Register for the satellite provision state change.
+     * Deprovision the device with the satellite provider.
+     * This is needed if the provider allows dynamic registration. Once deprovisioned,
+     * {@link SatelliteCallback.SatelliteProvisionStateListener#onSatelliteProvisionStateChanged}
+     * should report as deprovisioned.
+     * For provisioning satellite service, refer to
+     * {@link #provisionSatelliteService(String, CancellationSignal, Executor, Consumer)}.
      *
-     * @param executor - The executor on which the callback will be called.
-     * @param callback The callback to handle the satellite provision state changed event. This
-     *                 SatelliteCallback should implement the interface
-     *                 {@link SatelliteCallback.SatelliteProvisionStateListener}.
-     * @return The error code of the request.
+     * @param token The token of the device/subscription to be deprovisioned.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
+     *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError
-    public int registerForSatelliteProvisionStateChanged(
-            @NonNull Executor executor, @NonNull SatelliteCallback callback) {
+    public void deprovisionSatelliteService(@NonNull String token,
+            @NonNull @CallbackExecutor Executor executor,
+            @SatelliteError @NonNull Consumer<Integer> errorCodeListener) {
+        Objects.requireNonNull(token);
         Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
+                    }
+                };
+                telephony.deprovisionSatelliteService(mSubId, token, errorCallback);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("deprovisionSatelliteService RemoteException=" + ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Register for the satellite provision state change.
+     *
+     * @param executor The executor on which the callback and error code listener will be called.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
+     * @param callback The callback to handle the satellite provision state changed event. This
+     *                 SatelliteCallback should implement the interface
+     *                 {@link SatelliteCallback.SatelliteProvisionStateListener}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void registerForSatelliteProvisionStateChanged(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> errorCodeListener, @NonNull SatelliteCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
         Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 callback.init(executor);
-                return telephony.registerForSatelliteProvisionStateChanged(
-                        mSubId, callback.getCallbackStub());
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
+                    }
+                };
+                telephony.registerForSatelliteProvisionStateChanged(
+                        mSubId, errorCallback, callback.getCallbackStub());
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -594,33 +802,46 @@
             loge("registerForSatelliteProvisionStateChanged RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
      * Unregister for the satellite provision state change.
      *
      * @param callback The callback that was passed to
-     * {@link #registerForSatelliteProvisionStateChanged(Executor, SatelliteCallback)}
-     * @return The error code of the request.
+     * {@link #registerForSatelliteProvisionStateChanged(Executor, Consumer, SatelliteCallback)}.
+     * @param executor The executor on which the error code listener will be called.
+     * @param errorCodeListener Listener for the {@link SatelliteError} result of the operation.
+     *
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @SatelliteError
-    public int unregisterForSatelliteProvisionStateChanged(@NonNull SatelliteCallback callback) {
+    public void unregisterForSatelliteProvisionStateChanged(@NonNull SatelliteCallback callback,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> errorCodeListener) {
         Objects.requireNonNull(callback);
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(errorCodeListener);
 
         if (callback.getCallbackStub() == null) {
             loge("unregisterForSatelliteProvisionStateChanged: callbackStub is null");
-            return SATELLITE_INVALID_ARGUMENTS;
+            executor.execute(() -> Binder.withCleanCallingIdentity(
+                    () -> errorCodeListener.accept(SATELLITE_INVALID_ARGUMENTS)));
+            return;
         }
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return telephony.unregisterForSatelliteProvisionStateChanged(
-                        mSubId, callback.getCallbackStub());
+                IIntegerConsumer errorCallback = new IIntegerConsumer.Stub() {
+                    @Override
+                    public void accept(int result) {
+                        executor.execute(() -> Binder.withCleanCallingIdentity(
+                                () -> errorCodeListener.accept(result)));
+                    }
+                };
+                telephony.unregisterForSatelliteProvisionStateChanged(mSubId, errorCallback,
+                        callback.getCallbackStub());
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
@@ -628,43 +849,222 @@
             loge("unregisterForSatelliteProvisionStateChanged RemoteException: " + ex);
             ex.rethrowFromSystemServer();
         }
-        return SATELLITE_REQUEST_FAILED;
     }
 
     /**
-     * Get the list of provisioned satellite features.
+     * Request to get whether this device is provisioned with a satellite provider.
      *
-     * @param executor The executor to run callbacks on.
-     * @param resultListener The callback to get the list of provisioned features when the request
-     *                       returns success result.
+     * @param executor The executor on which the callback will be called.
+     * @param callback The callback object to which the result will be delivered.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
+     *                 will return a {@code boolean} with value {@code true} if the device is
+     *                 provisioned with a satellite provider and {@code false} otherwise.
+     *                 If the request is not successful, {@link OutcomeReceiver#onError(Throwable)}
+     *                 will return a {@link SatelliteException} with the {@link SatelliteError}.
+     *
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor,
+            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                ResultReceiver receiver = new ResultReceiver(null) {
+                    @Override
+                    protected void onReceiveResult(int resultCode, Bundle resultData) {
+                        if (resultCode == SATELLITE_ERROR_NONE) {
+                            if (resultData.containsKey(KEY_SATELLITE_PROVISIONED)) {
+                                boolean isSatelliteProvisioned =
+                                        resultData.getBoolean(KEY_SATELLITE_PROVISIONED);
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onResult(isSatelliteProvisioned)));
+                            } else {
+                                loge("KEY_SATELLITE_PROVISIONED does not exist.");
+                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                        callback.onError(
+                                                new SatelliteException(SATELLITE_REQUEST_FAILED))));
+                            }
+                        } else {
+                            executor.execute(() -> Binder.withCleanCallingIdentity(() ->
+                                    callback.onError(new SatelliteException(resultCode))));
+                        }
+                    }
+                };
+                telephony.requestIsSatelliteProvisioned(mSubId, receiver);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("requestIsSatelliteProvisioned() RemoteException: " + ex);
+            ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Register for listening to satellite modem state changes.
+     *
+     * @param executor - The executor on which the callback will be called.
+     * @param callback - The callback to handle the satellite state change event. This
+     *                 SatelliteCallback should implement the interface
+     *                 {@link SatelliteCallback.SatelliteStateListener}.
+     *
      * @return The error code of the request.
      * @throws SecurityException if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @SatelliteError
-    public int getProvisionedSatelliteFeatures(
-            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<int[]> resultListener) {
-        Objects.requireNonNull(resultListener);
+    public int registerForSatelliteModemStateChange(@NonNull @CallbackExecutor Executor executor,
+            @NonNull SatelliteCallback callback) {
         Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                IIntArrayConsumer callbackStub = new IIntArrayConsumer.Stub() {
-                    @Override
-                    public void accept(int[] result) {
-                        Binder.withCleanCallingIdentity(() -> {
-                            executor.execute(() -> resultListener.accept(result));
-                        });
-                    }
-                };
-                return telephony.getProvisionedSatelliteFeatures(mSubId, callbackStub);
+                callback.init(executor);
+                return telephony.registerForSatelliteModemStateChange(mSubId,
+                        callback.getCallbackStub());
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("getProvisionedSatelliteFeatures() RemoteException:" + ex);
+            loge("registerForSatelliteModemStateChange() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregister to stop listening to satellite modem state changes.
+     *
+     * @param callback - The callback that was passed to
+     * {@link #registerForSatelliteModemStateChange(Executor, SatelliteCallback)}
+     *
+     * @return The error code of the request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteError
+    public int unregisterForSatelliteModemStateChange(@NonNull SatelliteCallback callback) {
+        Objects.requireNonNull(callback);
+
+        if (callback.getCallbackStub() == null) {
+            loge("unregisterForSatelliteModemStateChange: callbackStub is null");
+            return SATELLITE_INVALID_ARGUMENTS;
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.unregisterForSatelliteModemStateChange(mSubId,
+                        callback.getCallbackStub());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForSatelliteModemStateChange() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_REQUEST_FAILED;
+    }
+
+    /**
+     * Register to receive incoming datagrams over satellite.
+     *
+     * @param datagramType - type of datagram
+     * @param executor - The executor on which the callback will be called.
+     * @param callback - The callback to handle incoming datagrams over satellite. This
+     *                 SatelliteCallback should implement the interface
+     *                 {@link SatelliteCallback.SatelliteDatagramListener}.
+     *
+     * @return The error code of the request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteError
+    public int registerForSatelliteDatagram(@DatagramType int datagramType,
+            @NonNull @CallbackExecutor Executor executor, @NonNull SatelliteCallback callback) {
+        Objects.requireNonNull(executor);
+        Objects.requireNonNull(callback);
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                callback.init(executor);
+                return telephony.registerForSatelliteDatagram(mSubId, datagramType,
+                            callback.getCallbackStub());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("registerForSatelliteDatagram() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_REQUEST_FAILED;
+    }
+
+    /**
+     * Unregister to stop receiving incoming datagrams over satellite.
+     *
+     * @param callback - The callback that was passed to
+     * {@link #registerForSatelliteDatagram(int, Executor, SatelliteCallback)}
+     *
+     * @return The error code of the request.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteError
+    public int unregisterForSatelliteDatagram(@NonNull SatelliteCallback callback) {
+        Objects.requireNonNull(callback);
+
+        if (callback.getCallbackStub() == null) {
+            loge("unregisterForSatelliteDatagram: callbackStub is null");
+            return SATELLITE_INVALID_ARGUMENTS;
+        }
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.unregisterForSatelliteDatagram(mSubId,
+                        callback.getCallbackStub());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("unregisterForSatelliteDatagram() RemoteException:" + ex);
+            ex.rethrowFromSystemServer();
+        }
+        return SATELLITE_REQUEST_FAILED;
+    }
+
+    /**
+     * Poll pending satellite datagrams over satellite.
+     *
+     * @return The result of the operation.
+     * @throws SecurityException if the caller doesn't have required permission.
+     * @throws IllegalStateException if the Telephony process is not currently available.
+     */
+    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+    @SatelliteError
+    public int pollPendingSatelliteDatagrams() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.pollPendingSatelliteDatagrams(mSubId);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            loge("pollPendingSatelliteDatagrams() RemoteException:" + ex);
             ex.rethrowFromSystemServer();
         }
         return SATELLITE_REQUEST_FAILED;
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index baf45cd..331ee6f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -68,7 +68,6 @@
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.telephony.ims.aidl.IRcsConfigCallback;
 import android.telephony.satellite.ISatelliteStateListener;
-import android.telephony.satellite.ISatelliteCapabilitiesConsumer;
 import android.telephony.satellite.SatelliteCapabilities;
 import com.android.ims.internal.IImsServiceFeatureCallback;
 import com.android.internal.telephony.CellNetworkScanResult;
@@ -76,7 +75,6 @@
 import com.android.internal.telephony.ICallForwardingInfoCallback;
 import com.android.internal.telephony.IccLogicalChannelRequest;
 import com.android.internal.telephony.IImsStateCallback;
-import com.android.internal.telephony.IIntArrayConsumer;
 import com.android.internal.telephony.IIntegerConsumer;
 import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.OperatorInfo;
@@ -2704,74 +2702,196 @@
     void getCarrierRestrictionStatus(IIntegerConsumer internalCallback, String packageName);
 
     /**
-     * Power on or off the satellite modem.
+     * Enable or disable the satellite modem.
+     *
+     * @param subId The subId of the subscription to enable or disable the satellite modem for.
+     * @param enable True to enable the satellite modem and false to disable.
+     * @param callback The callback to get the error code of the request.
      */
-    int setSatellitePower(int subId, boolean powerOn);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void setSatelliteEnabled(int subId, boolean enable, in IIntegerConsumer callback);
 
     /**
-     * Check whether the satellite modem is powered on.
+     * Request to get whether the satellite modem is enabled.
+     *
+     * @param subId The subId of the subscription to request whether satellite is enabled for.
+     * @param receiver Result receiver to get the error code of the request and whether the
+     *                 satellite modem is enabled.
      */
-    int isSatellitePowerOn(int subId, IBooleanConsumer internalCallback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestIsSatelliteEnabled(int subId, in ResultReceiver receiver);
 
     /**
-     * Check whether the satellite service is supported on the device.
+     * Request to get whether the satellite service is supported on the device.
+     *
+     * @param subId The subId of the subscription to check whether satellite is supported for.
+     * @param receiver Result receiver to get the error code of the request and whether the
+     *                 satellite service is supported on the device.
      */
-    int isSatelliteSupported(int subId, IBooleanConsumer internalCallback);
+    void requestIsSatelliteSupported(int subId, in ResultReceiver receiver);
 
     /**
-     * Get the capabilities of the satellite service.
+     * Request to get the capabilities of the satellite service.
+     *
+     * @param subId The subId of the subscription to get the capabilities for.
+     * @param receiver Result receiver to get the error code of the request and the requested
+     *                 capabilities of the satellite service.
      */
-    int getSatelliteCapabilities(int subId, ISatelliteCapabilitiesConsumer internalCallback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestSatelliteCapabilities(int subId, in ResultReceiver receiver);
 
     /**
      * Start receiving satellite pointing updates.
+     *
+     * @param subId The subId of the subscription to stop satellite position updates for.
+     * @param errorCallback The callback to get the error code of the request.
+     * @param callback The callback to handle position updates.
      */
-    int startSatellitePositionUpdates(int subId, in ISatelliteStateListener callback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void startSatellitePositionUpdates(int subId, in IIntegerConsumer errorCallback,
+            in ISatelliteStateListener callback);
 
     /**
      * Stop receiving satellite pointing updates.
+     *
+     * @param subId The subId of the subscritpion to stop satellite position updates for.
+     * @param errorCallback The callback to get the error code of the request.
+     * @param callback The callback that was passed to startSatellitePositionUpdates.
      */
-    int stopSatellitePositionUpdates(int subId, ISatelliteStateListener callback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void stopSatellitePositionUpdates(int subId, in IIntegerConsumer errorCallback,
+            in ISatelliteStateListener callback);
 
     /**
-     * Get maximum number of characters per text message on satellite.
+     * Request to get the maximum number of characters per text message on satellite.
+     *
+     * @param subId The subId to get the maximum number of characters for.
+     * @param receiver Result receiver to get the error code of the request and the requested
+     *                 maximum number of characters per text message on satellite.
      */
-    int getMaxCharactersPerSatelliteTextMessage(int subId, IIntegerConsumer internalCallback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestMaxCharactersPerSatelliteTextMessage(int subId, in ResultReceiver receiver);
 
     /**
      * Register the subscription with a satellite provider.
      * This is needed to register the subscription if the provider allows dynamic registration.
      *
      * @param subId The subId of the subscription to be provisioned.
-     * @param features List of features to be provisioned.
+     * @param token The token to be used as a unique identifier for provisioning with satellite
+     *              gateway.
      * @param callback The callback to get the error code of the request.
+     *
      * @return The signal transport used by callers to cancel the provision request.
      */
-    ICancellationSignal provisionSatelliteService(int subId, in int[] features,
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    ICancellationSignal provisionSatelliteService(int subId, in String token,
             in IIntegerConsumer callback);
 
     /**
+     * Unregister the subscription with the satellite provider.
+     * This is needed to unregister the subscription if the provider allows dynamic registration.
+     * Once deprovisioned,
+     * {@link SatelliteCallback.SatelliteProvisionStateListener#onSatelliteProvisionStateChanged}
+     * should report as deprovisioned.
+     *
+     * @param subId The subId of the subscription to be deprovisioned.
+     * @param token The token of the device/subscription to be deprovisioned.
+     * @param callback The callback to get the error code of the request.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void deprovisionSatelliteService(int subId, in String token, in IIntegerConsumer callback);
+
+
+    /**
      * Register for the satellite provision state change.
      *
-     * @param subId The subId of the subscription to be provisioned.
+     * @param subId The subId of the subscription to register for provision state changes for.
+     * @param errorCallback The callback to get the error code of the request.
      * @param callback The callback to handle the satellite provision state changed event.
      */
-    int registerForSatelliteProvisionStateChanged(int subId, ISatelliteStateListener callback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void registerForSatelliteProvisionStateChanged(int subId,
+            in IIntegerConsumer errorCallback, in ISatelliteStateListener callback);
 
     /**
      * Unregister for the satellite provision state change.
      *
      * @param subId The subId of the subscription associated with the satellite service.
-     * @param callback The callback that was passed to
-     *                   registerForSatelliteProvisionStateChanged.
+     * @param errorCallback The callback to get the error code of the request.
+     * @param callback The callback that was passed to registerForSatelliteProvisionStateChanged.
      */
-    int unregisterForSatelliteProvisionStateChanged(int subId, ISatelliteStateListener callback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void unregisterForSatelliteProvisionStateChanged(int subId,
+            in IIntegerConsumer errorCallback, in ISatelliteStateListener callback);
 
     /**
-     * Get the list of provisioned satellite features.
+     * Request to get whether the device is provisioned with a satellite provider.
      *
-     * @param subId The subId of the subscription to be provisioned.
-     * @param callback The callback to get the list of provisioned satellite features.
+     * @param subId The subId of the subscription to get whether the device is provisioned for.
+     * @param receiver Result receiver to get the error code of the request and whether the
+     *                 device is provisioned with a satellite provider.
      */
-    int getProvisionedSatelliteFeatures(int subId, IIntArrayConsumer callback);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    void requestIsSatelliteProvisioned(int subId, in ResultReceiver receiver);
+
+    /**
+     * Register for listening to satellite modem state changes.
+     *
+     * @param subId - The subId of the subscription used for listening to satellite state changes.
+     * @param callback - The callback to handle the satellite state changed event.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int registerForSatelliteModemStateChange(int subId, ISatelliteStateListener callback);
+
+    /**
+     * Unregister to stop listening to satellite modem state changes.
+     *
+     * @param subId - The subId of the subscription associated with the satellite service.
+     * @param callback - The callback that was passed to registerForSatelliteStateChange.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+            + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int unregisterForSatelliteModemStateChange(int subId, ISatelliteStateListener callback);
+
+   /**
+     * Register to receive incoming datagrams over satellite.
+     *
+     * @param subId - The subId of the subscription used for receiving datagrams.
+     * @param datagramType - type of datagram
+     * @param callback - The callback to receive incoming datagrams.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+                + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int registerForSatelliteDatagram(int subId, int datagramType, ISatelliteStateListener callback);
+
+   /**
+     * Unregister to stop receiving incoming datagrams over satellite.
+     *
+     * @param subId - The subId of the subscription associated with the satellite service.
+     * @param callback - The callback that was passed to registerForSatelliteDatagram.
+     */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+                + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int unregisterForSatelliteDatagram(int subId, ISatelliteStateListener callback);
+
+   /**
+    * Poll pending satellite datagrams over satellite.
+    *
+    * @param subId - The subId of the subscription used for receiving datagrams.
+    */
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+                + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+    int pollPendingSatelliteDatagrams(int subId);
 }
diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
index 3ea9651..48d050c 100644
--- a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
+++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java
@@ -16,7 +16,9 @@
 
 package android.view.choreographertests;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -63,6 +65,7 @@
     private DisplayManager mDisplayManager;
     private SurfaceView mSurfaceView;
     private SurfaceHolder mSurfaceHolder;
+    private Choreographer mChoreographer;
     private boolean mIsFirstCallback = true;
     private int mCallbackMissedCounter = 0;
 
@@ -127,37 +130,60 @@
 
     @Test
     public void testCreateChoreographer() {
+        Looper testLooper = Looper.myLooper();
         mScenario.onActivity(activity -> {
             if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) {
                 fail("Unable to create surface within 1 Second");
             }
             SurfaceControl sc = mSurfaceView.getSurfaceControl();
+            mChoreographer = sc.getChoreographer();
             mTestCompleteSignal.countDown();
             SurfaceControl sc1 = new SurfaceControl(sc, "AttachedChoreographerTests");
             // Create attached choreographer with getChoreographer
-            sc1.getChoreographer();
+            Choreographer choreographer1 = sc1.getChoreographer();
             assertTrue(sc1.hasChoreographer());
             assertTrue(sc1.isValid());
-            sc1.release();
+            assertEquals(choreographer1, sc1.getChoreographer());
+            assertEquals(choreographer1, sc1.getChoreographer(Looper.myLooper()));
+            assertEquals(choreographer1, sc1.getChoreographer(Looper.getMainLooper()));
+            assertThrows(IllegalStateException.class, () -> sc1.getChoreographer(testLooper));
 
             SurfaceControl sc2 = new SurfaceControl(sc, "AttachedChoreographerTests");
             // Create attached choreographer with Looper.myLooper
-            sc2.getChoreographer(Looper.myLooper());
+            Choreographer choreographer2 = sc2.getChoreographer(Looper.myLooper());
             assertTrue(sc2.hasChoreographer());
             assertTrue(sc2.isValid());
-            sc2.release();
+            assertEquals(choreographer2, sc2.getChoreographer(Looper.myLooper()));
+            assertEquals(choreographer2, sc2.getChoreographer(Looper.getMainLooper()));
+            assertEquals(choreographer2, sc2.getChoreographer());
+            assertThrows(IllegalStateException.class, () -> sc2.getChoreographer(testLooper));
 
             SurfaceControl sc3 = new SurfaceControl(sc, "AttachedChoreographerTests");
             // Create attached choreographer with Looper.myLooper
-            sc3.getChoreographer(Looper.getMainLooper());
+            Choreographer choreographer3 = sc3.getChoreographer(Looper.getMainLooper());
             assertTrue(sc3.hasChoreographer());
             assertTrue(sc3.isValid());
+            assertEquals(choreographer3, sc3.getChoreographer(Looper.getMainLooper()));
+            assertEquals(choreographer3, sc3.getChoreographer(Looper.myLooper()));
+            assertEquals(choreographer3, sc3.getChoreographer());
+            assertThrows(IllegalStateException.class, () -> sc3.getChoreographer(testLooper));
+
+            assertNotEquals(choreographer1, choreographer2);
+            assertNotEquals(choreographer1, choreographer3);
+            assertNotEquals(choreographer2, choreographer3);
+            sc1.release();
+            sc2.release();
             sc3.release();
             mTestCompleteSignal.countDown();
         });
         if (waitForCountDown(mTestCompleteSignal, /* timeoutInSeconds */ 2L)) {
             fail("Test not finished in 2 Seconds");
         }
+        SurfaceControl surfaceControl = mSurfaceView.getSurfaceControl();
+        assertTrue(surfaceControl.hasChoreographer());
+        assertEquals(mChoreographer, surfaceControl.getChoreographer());
+        assertThrows(IllegalStateException.class,
+                () -> surfaceControl.getChoreographer(testLooper));
     }
 
     @Test
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index aa337e5..0d6dc35 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -50,6 +50,7 @@
     ],
     target: {
         windows: {
+            compile_multilib: "64",
             enabled: true,
             cflags: ["-Wno-maybe-uninitialized"],
             ldflags: ["-static"],
diff --git a/tools/lint/fix/Android.bp b/tools/lint/fix/Android.bp
index 5f6c6f7..7375c16 100644
--- a/tools/lint/fix/Android.bp
+++ b/tools/lint/fix/Android.bp
@@ -25,4 +25,10 @@
     name: "lint_fix",
     main: "lint_fix.py",
     srcs: ["lint_fix.py"],
+    libs: ["soong_lint_fix"],
+}
+
+python_library_host {
+    name: "soong_lint_fix",
+    srcs: ["soong_lint_fix.py"],
 }
diff --git a/tools/lint/fix/lint_fix.py b/tools/lint/fix/lint_fix.py
index 0f94bd2..1c83f7b 100644
--- a/tools/lint/fix/lint_fix.py
+++ b/tools/lint/fix/lint_fix.py
@@ -1,77 +1,29 @@
-import argparse
-import os
-import sys
+#  Copyright (C) 2023 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.
+#
+#  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.
 
-ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
-PATH_PREFIX = "out/soong/.intermediates"
-PATH_SUFFIX = "android_common/lint"
-FIX_DIR = "suggested-fixes"
+from soong_lint_fix import SoongLintFix
 
-parser = argparse.ArgumentParser(description="""
-This is a python script that applies lint fixes to the platform:
-1. Set up the environment, etc.
-2. Build the lint and run it.
-3. Unpack soong's intermediate zip containing source files modified by lint.
-4. Copy the modified files back into the tree.
-
-**Gotcha**: You must have run `source build/envsetup.sh` and `lunch` \
-so that the `ANDROID_BUILD_TOP` environment variable has been set.
-Alternatively, set it manually in your shell.
-""", formatter_class=argparse.RawTextHelpFormatter)
-
-parser.add_argument('build_path', metavar='build_path', type=str,
-                    help='The build module to run '
-                         '(e.g. frameworks/base/framework-minus-apex or '
-                         'frameworks/base/services/core/services.core.unboosted)')
-
-parser.add_argument('--check', metavar='check', type=str,
-                    help='Which lint to run. Passed to the ANDROID_LINT_CHECK environment variable.')
-
-parser.add_argument('--dry-run', dest='dry_run', action='store_true',
-                    help='Just print the resulting shell script instead of running it.')
-
-parser.add_argument('--no-fix', dest='no_fix', action='store_true',
-                    help='Just build and run the lint, do NOT apply the fixes.')
-
-args = parser.parse_args()
-
-path = f"{PATH_PREFIX}/{args.build_path}/{PATH_SUFFIX}"
-target = f"{path}/lint-report.html"
-
-commands = []
-
-if not args.dry_run:
-    commands += [f"export ANDROID_BUILD_TOP={ANDROID_BUILD_TOP}"]
-
-if args.check:
-    commands += [f"export ANDROID_LINT_CHECK={args.check}"]
-
-commands += [
-    "cd $ANDROID_BUILD_TOP",
-    "source build/envsetup.sh",
-    f"rm {target}",  # remove the file first so soong doesn't think there is no work to do
-    f"rm {path}/{FIX_DIR}.zip", # remove in case there are fixes from a prior run that we don't want applied if this run fails
-    f"m {target}",
-]
-
-if not args.no_fix:
-    commands += [
-        f"cd {path}",
-        f"unzip {FIX_DIR}.zip -d {FIX_DIR}",
-        f"cd {FIX_DIR}",
-        # Find all the java files in the fix directory, excluding the ./out subdirectory,
-        # and copy them back into the same path within the tree.
-        f"find . -path ./out -prune -o -name '*.java' -print | xargs -n 1 sh -c 'cp $1 $ANDROID_BUILD_TOP/$1' --",
-        f"rm -rf {FIX_DIR}"
-    ]
-
-if args.dry_run:
-    print("(\n" + ";\n".join(commands) + "\n)")
-    sys.exit(0)
-
-with_echo = []
-for c in commands:
-    with_echo.append(f'echo "{c}"')
-    with_echo.append(c)
-
-os.system("(\n" + ";\n".join(with_echo) + "\n)")
+SoongLintFix().run()
diff --git a/tools/lint/fix/soong_lint_fix.py b/tools/lint/fix/soong_lint_fix.py
new file mode 100644
index 0000000..3308df6
--- /dev/null
+++ b/tools/lint/fix/soong_lint_fix.py
@@ -0,0 +1,169 @@
+#  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.
+
+import argparse
+import os
+import subprocess
+import sys
+
+ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP")
+PATH_PREFIX = "out/soong/.intermediates"
+PATH_SUFFIX = "android_common/lint"
+FIX_DIR = "suggested-fixes"
+
+class SoongLintFix:
+    """
+    This class creates a command line tool that will
+    apply lint fixes to the platform via the necessary
+    combination of soong and shell commands.
+
+    It provides some basic hooks for experimental code
+    to tweak the generation of the resulting shell script.
+
+    By default, it will apply lint fixes using the intermediate `suggested-fixes`
+    directory that soong creates during its invocation of lint.
+
+    The default argument parser configures a number of command line arguments to
+    facilitate running lint via soong.
+
+    Basic usage:
+    ```
+    from soong_lint_fix import SoongLintFix
+
+    SoongLintFix().run()
+    ```
+    """
+    def __init__(self):
+        self._commands = None
+        self._args = None
+        self._path = None
+        self._target = None
+        self._parser = _setup_parser()
+
+
+    def add_argument(self, *args, **kwargs):
+        """
+        If necessary, add arguments to the underlying argparse.ArgumentParser before running
+        """
+        self._parser.add_argument(*args, **kwargs)
+
+
+    def run(self, add_setup_commands=None, override_fix_commands=None):
+        """
+        Run the script
+        :param add_setup_commands: OPTIONAL function to add additional setup commands
+            passed the command line arguments, path, and build target
+            must return a list of strings (the additional commands)
+        :param override_fix_commands: OPTIONAL function to override the fix commands
+            passed the command line arguments, path, and build target
+            must return a list of strings (the fix commands)
+        """
+        self._setup()
+        if add_setup_commands:
+            self._commands += add_setup_commands(self._args, self._path, self._target)
+
+        self._add_lint_report_commands()
+
+        if not self._args.no_fix:
+            if override_fix_commands:
+                self._commands += override_fix_commands(self._args, self._path, self._target)
+            else:
+                self._commands += [
+                    f"cd {self._path}",
+                    f"unzip {FIX_DIR}.zip -d {FIX_DIR}",
+                    f"cd {FIX_DIR}",
+                    # Find all the java files in the fix directory, excluding the ./out subdirectory,
+                    # and copy them back into the same path within the tree.
+                    f"find . -path ./out -prune -o -name '*.java' -print | xargs -n 1 sh -c 'cp $1 $ANDROID_BUILD_TOP/$1 || exit 255' --",
+                    f"rm -rf {FIX_DIR}"
+                ]
+
+
+        if self._args.dry_run:
+            print(self._get_commands_str())
+        else:
+            self._execute()
+
+
+    def _setup(self):
+        self._args = self._parser.parse_args()
+        self._commands = []
+        self._path = f"{PATH_PREFIX}/{self._args.build_path}/{PATH_SUFFIX}"
+        self._target = f"{self._path}/lint-report.html"
+
+        if not self._args.dry_run:
+            self._commands += [f"export ANDROID_BUILD_TOP={ANDROID_BUILD_TOP}"]
+
+        if self._args.check:
+            self._commands += [f"export ANDROID_LINT_CHECK={self._args.check}"]
+
+
+    def _add_lint_report_commands(self):
+        self._commands += [
+            "cd $ANDROID_BUILD_TOP",
+            "source build/envsetup.sh",
+            # remove the file first so soong doesn't think there is no work to do
+            f"rm {self._target}",
+            # remove in case there are fixes from a prior run,
+            # that we don't want applied if this run fails
+            f"rm {self._path}/{FIX_DIR}.zip",
+            f"m {self._target}",
+        ]
+
+
+    def _get_commands_str(self):
+        prefix = "(\n"
+        delimiter = ";\n"
+        suffix = "\n)"
+        return f"{prefix}{delimiter.join(self._commands)}{suffix}"
+
+
+    def _execute(self, with_echo=True):
+        if with_echo:
+            exec_commands = []
+            for c in self._commands:
+                exec_commands.append(f'echo "{c}"')
+                exec_commands.append(c)
+            self._commands = exec_commands
+
+        subprocess.call(self._get_commands_str(), executable='/bin/bash', shell=True)
+
+
+def _setup_parser():
+    parser = argparse.ArgumentParser(description="""
+        This is a python script that applies lint fixes to the platform:
+        1. Set up the environment, etc.
+        2. Run lint on the specified target.
+        3. Copy the modified files, from soong's intermediate directory, back into the tree.
+
+        **Gotcha**: You must have run `source build/envsetup.sh` and `lunch`
+        so that the `ANDROID_BUILD_TOP` environment variable has been set.
+        Alternatively, set it manually in your shell.
+        """, formatter_class=argparse.RawTextHelpFormatter)
+
+    parser.add_argument('build_path', metavar='build_path', type=str,
+                        help='The build module to run '
+                             '(e.g. frameworks/base/framework-minus-apex or '
+                             'frameworks/base/services/core/services.core.unboosted)')
+
+    parser.add_argument('--check', metavar='check', type=str,
+                        help='Which lint to run. Passed to the ANDROID_LINT_CHECK environment variable.')
+
+    parser.add_argument('--dry-run', dest='dry_run', action='store_true',
+                        help='Just print the resulting shell script instead of running it.')
+
+    parser.add_argument('--no-fix', dest='no_fix', action='store_true',
+                        help='Just build and run the lint, do NOT apply the fixes.')
+
+    return parser
diff --git a/wifi/java/src/android/net/wifi/nl80211/PnoSettings.java b/wifi/java/src/android/net/wifi/nl80211/PnoSettings.java
index 00ebe62..2f15066 100644
--- a/wifi/java/src/android/net/wifi/nl80211/PnoSettings.java
+++ b/wifi/java/src/android/net/wifi/nl80211/PnoSettings.java
@@ -19,9 +19,12 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import androidx.annotation.RequiresApi;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
@@ -38,6 +41,8 @@
     private int mMin2gRssi;
     private int mMin5gRssi;
     private int mMin6gRssi;
+    private int mScanIterations;
+    private int mScanIntervalMultiplier;
     private List<PnoNetwork> mPnoNetworks;
 
     /** Construct an uninitialized PnoSettings object */
@@ -122,6 +127,46 @@
     }
 
     /**
+     * Get the requested PNO scan iterations.
+     *
+     * @return PNO scan iterations.
+     */
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public int getScanIterations() {
+        return mScanIterations;
+    }
+
+    /**
+     * Set the requested PNO scan iterations.
+     *
+     * @param scanIterations the PNO scan iterations.
+     */
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public void setScanIterations(int scanIterations) {
+        this.mScanIterations = scanIterations;
+    }
+
+    /**
+     * Get the requested PNO scan interval multiplier.
+     *
+     * @return PNO scan interval multiplier.
+     */
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public int getScanIntervalMultiplier() {
+        return mScanIntervalMultiplier;
+    }
+
+    /**
+     * Set the requested PNO scan interval multiplier.
+     *
+     * @param scanIntervalMultiplier the PNO scan interval multiplier.
+     */
+    @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public void setScanIntervalMultiplier(int scanIntervalMultiplier) {
+        this.mScanIntervalMultiplier = scanIntervalMultiplier;
+    }
+
+    /**
      * Return the configured list of specific networks to search for in a PNO scan.
      *
      * @return A list of {@link PnoNetwork} objects, possibly empty if non configured.
@@ -156,13 +201,16 @@
                 && mMin2gRssi == settings.mMin2gRssi
                 && mMin5gRssi == settings.mMin5gRssi
                 && mMin6gRssi == settings.mMin6gRssi
+                && mScanIterations == settings.mScanIterations
+                && mScanIntervalMultiplier == settings.mScanIntervalMultiplier
                 && mPnoNetworks.equals(settings.mPnoNetworks);
     }
 
     /** override hash code */
     @Override
     public int hashCode() {
-        return Objects.hash(mIntervalMs, mMin2gRssi, mMin5gRssi, mMin6gRssi, mPnoNetworks);
+        return Objects.hash(mIntervalMs, mMin2gRssi, mMin5gRssi, mMin6gRssi,
+                mScanIterations, mScanIntervalMultiplier, mPnoNetworks);
     }
 
     /** implement Parcelable interface */
@@ -181,6 +229,8 @@
         out.writeInt(mMin2gRssi);
         out.writeInt(mMin5gRssi);
         out.writeInt(mMin6gRssi);
+        out.writeInt(mScanIterations);
+        out.writeInt(mScanIntervalMultiplier);
         out.writeTypedList(mPnoNetworks);
     }
 
@@ -194,6 +244,8 @@
             result.mMin2gRssi = in.readInt();
             result.mMin5gRssi = in.readInt();
             result.mMin6gRssi = in.readInt();
+            result.mScanIterations = in.readInt();
+            result.mScanIntervalMultiplier = in.readInt();
 
             result.mPnoNetworks = new ArrayList<>();
             in.readTypedList(result.mPnoNetworks, PnoNetwork.CREATOR);
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
index a40049b..10ef066 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityService.java
@@ -101,13 +101,13 @@
             @Override
             public void registerCallback(ISharedConnectivityCallback callback) {
                 checkPermissions();
-                mHandler.post(() -> registerCallback(callback));
+                mHandler.post(() -> onRegisterCallback(callback));
             }
 
             @Override
             public void unregisterCallback(ISharedConnectivityCallback callback) {
                 checkPermissions();
-                mHandler.post(() -> unregisterCallback(callback));
+                mHandler.post(() -> onUnregisterCallback(callback));
             }
 
             @Override
@@ -147,7 +147,7 @@
         };
     }
 
-    private void registerCallback(ISharedConnectivityCallback callback) {
+    private void onRegisterCallback(ISharedConnectivityCallback callback) {
         // Listener gets triggered on first register using cashed data
         if (!notifyTetherNetworkUpdate(callback) || !notifyKnownNetworkUpdate(callback)
                 || !notifySettingsStateUpdate(callback)
@@ -167,7 +167,7 @@
         }
     }
 
-    private void unregisterCallback(ISharedConnectivityCallback callback) {
+    private void onUnregisterCallback(ISharedConnectivityCallback callback) {
         DeathRecipient deathRecipient = mDeathRecipientMap.get(callback);
         if (deathRecipient != null) {
             callback.asBinder().unlinkToDeath(deathRecipient, 0);