Merge "Listen for changes to IME and show/hide nav bar"
diff --git a/Android.bp b/Android.bp
index 1023b48..9d05ffd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,30 +25,110 @@
 //
 // READ ME: ########################################################
 
+filegroup {
+    name: "framework-defaults-java-srcs",
+    srcs: [
+        // java sources under this directory
+        "core/java/**/*.java",
+        "drm/java/**/*.java",
+        "graphics/java/**/*.java",
+        "keystore/java/**/*.java",
+        "location/java/**/*.java",
+        "lowpan/java/**/*.java",
+        "media/java/**/*.java",
+        "media/mca/effect/java/**/*.java",
+        "media/mca/filterfw/java/**/*.java",
+        "media/mca/filterpacks/java/**/*.java",
+        "opengl/java/**/*.java",
+        "rs/java/**/*.java",
+        "sax/java/**/*.java",
+        "telecomm/java/**/*.java",
+        "telephony/java/**/*.java",
+        "wifi/java/**/*.java",
+    ],
+}
+
+// TODO(b/70046217): make these as filegroups where the base directory for aidl files
+// is given as 'path'. Eliminate the need for aidl_local_include_dirs.
+framework_srcs = [
+    // aidl under this directory
+    // b/70046217#comment15 These MUST come after all java srcs.
+    // TODO(b/70046217) remove the above requirement
+    "core/java/**/*.aidl",
+    "graphics/java/**/*.aidl",
+    "keystore/java/**/*.aidl",
+    "location/java/**/*.aidl",
+    "lowpan/java/**/*.aidl",
+    "media/java/**/*.aidl",
+    "packages/services/PacProcessor/**/*.aidl",
+    "packages/services/Proxy/**/*.aidl",
+    "telecomm/java/**/*.aidl",
+    "telephony/java/**/*.aidl",
+    "wifi/java/**/*.aidl",
+
+    // aidl from external directories
+    ":dumpstate_aidl",
+    ":gatekeeper_aidl",
+    ":gsiservice_aidl",
+    ":incidentcompanion_aidl",
+    ":installd_aidl",
+    ":keystore_aidl",
+    ":libaudioclient_aidl",
+    ":libbinder_aidl",
+    ":libbluetooth-binder-aidl",
+    ":libcamera_client_aidl",
+    ":libcamera_client_framework_aidl",
+    ":libupdate_engine_aidl",
+    ":storaged_aidl",
+    ":vold_aidl",
+
+    // etc.
+    "core/java/**/*.logtags",
+    ":framework-javastream-protos",
+    ":framework-statslog-gen",
+]
+
+framework_aidl_local_include_dirs = [
+    "core/java",
+    "drm/java",
+    "graphics/java",
+    "keystore/java",
+    "location/java",
+    "lowpan/java",
+    "media/java",
+    "media/apex/java",
+    "media/mca/effect/java",
+    "media/mca/filterfw/java",
+    "media/mca/filterpacks/java",
+    "opengl/java",
+    "rs/java",
+    "sax/java",
+    "telecomm/java",
+    "telephony/java",
+    "wifi/java",
+]
+
+framework_aidl_external_include_dirs = [
+    "frameworks/av/camera/aidl",
+    "frameworks/av/media/libaudioclient/aidl",
+    "frameworks/native/aidl/binder",
+    "frameworks/native/aidl/gui",
+    "frameworks/native/cmds/dumpstate/binder",
+    "frameworks/native/libs/incidentcompanion/binder",
+    "system/bt/binder",
+    "system/core/gatekeeperd/binder",
+    "system/core/storaged/binder",
+    "system/gsid/aidl",
+    "system/security/keystore/binder",
+    "system/update_engine/binder_bindings",
+    "system/vold/binder",
+]
+
 java_defaults {
     name: "framework-aidl-export-defaults",
 
     aidl: {
-        export_include_dirs: [
-            // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
-            "core/java",
-            "graphics/java",
-            "location/java",
-            "lowpan/java",
-            "media/java",
-            "media/apex/java",
-            "media/mca/effect/java",
-            "media/mca/filterfw/java",
-            "media/mca/filterpacks/java",
-            "drm/java",
-            "opengl/java",
-            "sax/java",
-            "telecomm/java",
-            "telephony/java",
-            "wifi/java",
-            "keystore/java",
-            "rs/java",
-        ],
+        export_include_dirs: framework_aidl_local_include_dirs,
     },
 }
 
@@ -58,692 +138,12 @@
     installable: true,
 
     srcs: [
-        // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
-        "core/java/**/*.java",
-        "graphics/java/**/*.java",
-        "location/java/**/*.java",
-        "lowpan/java/**/*.java",
-        "media/java/**/*.java",
-        "media/mca/effect/java/**/*.java",
-        "media/mca/filterfw/java/**/*.java",
-        "media/mca/filterpacks/java/**/*.java",
-        "drm/java/**/*.java",
-        "opengl/java/**/*.java",
-        "sax/java/**/*.java",
-        "telecomm/java/**/*.java",
-        "telephony/java/**/*.java",
-        "wifi/java/**/*.java",
-        "keystore/java/**/*.java",
-        "rs/java/**/*.java",
-
-        ":framework-javastream-protos",
-
-        "core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl",
-        "core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl",
-        "core/java/android/accounts/IAccountManager.aidl",
-        "core/java/android/accounts/IAccountManagerResponse.aidl",
-        "core/java/android/accounts/IAccountAuthenticator.aidl",
-        "core/java/android/accounts/IAccountAuthenticatorResponse.aidl",
-        "core/java/android/app/IActivityController.aidl",
-        "core/java/android/app/IActivityManager.aidl",
-        "core/java/android/app/IActivityPendingResult.aidl",
-        "core/java/android/app/IActivityTaskManager.aidl",
-        "core/java/android/app/IAlarmCompleteListener.aidl",
-        "core/java/android/app/IAlarmListener.aidl",
-        "core/java/android/app/IAlarmManager.aidl",
-        "core/java/android/app/IAppTask.aidl",
-        "core/java/android/app/IApplicationThread.aidl",
-        "core/java/android/app/IAssistDataReceiver.aidl",
-        "core/java/android/app/ITaskStackListener.aidl",
-        "core/java/android/app/IBackupAgent.aidl",
-        "core/java/android/app/IEphemeralResolver.aidl",
-        "core/java/android/app/IInstantAppResolver.aidl",
-        "core/java/android/app/IInstrumentationWatcher.aidl",
-        "core/java/android/app/INotificationManager.aidl",
-        "core/java/android/app/IProcessObserver.aidl",
-        "core/java/android/app/IRequestFinishCallback.aidl",
-        "core/java/android/app/ISearchManager.aidl",
-        "core/java/android/app/ISearchManagerCallback.aidl",
-        "core/java/android/app/IServiceConnection.aidl",
-        "core/java/android/app/IStopUserCallback.aidl",
-        "core/java/android/app/ITransientNotification.aidl",
-        "core/java/android/app/IUidObserver.aidl",
-        "core/java/android/app/IUiAutomationConnection.aidl",
-        "core/java/android/app/IUiModeManager.aidl",
-        "core/java/android/app/IUriGrantsManager.aidl",
-        "core/java/android/app/IUserSwitchObserver.aidl",
-        "core/java/android/app/IWallpaperManager.aidl",
-        "core/java/android/app/IWallpaperManagerCallback.aidl",
-        "core/java/android/app/admin/IDeviceAdminService.aidl",
-        "core/java/android/app/admin/IDevicePolicyManager.aidl",
-        "core/java/android/app/admin/StartInstallingUpdateCallback.aidl",
-        "core/java/android/app/trust/IStrongAuthTracker.aidl",
-        "core/java/android/app/trust/ITrustManager.aidl",
-        "core/java/android/app/trust/ITrustListener.aidl",
-        "core/java/android/app/backup/IBackupCallback.aidl",
-        "core/java/android/app/backup/IBackupManager.aidl",
-        "core/java/android/app/backup/IBackupObserver.aidl",
-        "core/java/android/app/backup/IBackupManagerMonitor.aidl",
-        "core/java/android/app/backup/IFullBackupRestoreObserver.aidl",
-        "core/java/android/app/backup/IRestoreObserver.aidl",
-        "core/java/android/app/backup/IRestoreSession.aidl",
-        "core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
-        "core/java/android/app/contentsuggestions/IClassificationsCallback.aidl",
-        "core/java/android/app/contentsuggestions/IContentSuggestionsManager.aidl",
-        "core/java/android/app/contentsuggestions/ISelectionsCallback.aidl",
-        "core/java/android/app/prediction/IPredictionCallback.aidl",
-        "core/java/android/app/prediction/IPredictionManager.aidl",
-        "core/java/android/app/role/IOnRoleHoldersChangedListener.aidl",
-        "core/java/android/app/role/IRoleController.aidl",
-        "core/java/android/app/role/IRoleManager.aidl",
-        "core/java/android/app/slice/ISliceManager.aidl",
-        "core/java/android/app/slice/ISliceListener.aidl",
-        "core/java/android/app/timedetector/ITimeDetectorService.aidl",
-        "core/java/android/app/timezone/ICallback.aidl",
-        "core/java/android/app/timezone/IRulesManager.aidl",
-        "core/java/android/app/usage/ICacheQuotaService.aidl",
-        "core/java/android/app/usage/IStorageStatsManager.aidl",
-        "core/java/android/app/usage/IUsageStatsManager.aidl",
-        ":libbluetooth-binder-aidl",
-        "core/java/android/content/IClipboard.aidl",
-        "core/java/android/content/IContentService.aidl",
-        "core/java/android/content/IIntentReceiver.aidl",
-        "core/java/android/content/IIntentSender.aidl",
-        "core/java/android/content/IOnPrimaryClipChangedListener.aidl",
-        "core/java/android/content/IRestrictionsManager.aidl",
-        "core/java/android/content/ISyncAdapter.aidl",
-        "core/java/android/content/ISyncAdapterUnsyncableAccountCallback.aidl",
-        "core/java/android/content/ISyncContext.aidl",
-        "core/java/android/content/ISyncServiceAdapter.aidl",
-        "core/java/android/content/ISyncStatusObserver.aidl",
-        "core/java/android/content/om/IOverlayManager.aidl",
-        "core/java/android/content/pm/ICrossProfileApps.aidl",
-        "core/java/android/content/pm/IDexModuleRegisterCallback.aidl",
-        "core/java/android/content/pm/ILauncherApps.aidl",
-        "core/java/android/content/pm/IOnAppsChangedListener.aidl",
-        "core/java/android/content/pm/IOtaDexopt.aidl",
-        "core/java/android/content/pm/IPackageDataObserver.aidl",
-        "core/java/android/content/pm/IPackageDeleteObserver.aidl",
-        "core/java/android/content/pm/IPackageDeleteObserver2.aidl",
-        "core/java/android/content/pm/IPackageInstallObserver2.aidl",
-        "core/java/android/content/pm/IPackageInstaller.aidl",
-        "core/java/android/content/pm/IPackageInstallerCallback.aidl",
-        "core/java/android/content/pm/IPackageInstallerSession.aidl",
-        "core/java/android/content/pm/IPackageManager.aidl",
-        ":libbinder_aidl",
-        "core/java/android/content/pm/IPackageMoveObserver.aidl",
-        "core/java/android/content/pm/IPackageStatsObserver.aidl",
-        "core/java/android/content/pm/IPinItemRequest.aidl",
-        "core/java/android/content/pm/IShortcutService.aidl",
-        "core/java/android/content/pm/dex/IArtManager.aidl",
-        "core/java/android/content/pm/dex/ISnapshotRuntimeProfileCallback.aidl",
-        "core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl",
-        "core/java/android/content/rollback/IRollbackManager.aidl",
-        "core/java/android/database/IContentObserver.aidl",
-        "core/java/android/debug/IAdbManager.aidl",
-        "core/java/android/debug/IAdbTransport.aidl",
-        ":libcamera_client_aidl",
-        ":libcamera_client_framework_aidl",
-        "core/java/android/hardware/IConsumerIrService.aidl",
-        "core/java/android/hardware/ISerialManager.aidl",
-        "core/java/android/hardware/biometrics/IBiometricConfirmDeviceCredentialCallback.aidl",
-        "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
-        "core/java/android/hardware/biometrics/IBiometricService.aidl",
-        "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
-        "core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl",
-        "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
-        "core/java/android/hardware/display/IColorDisplayManager.aidl",
-        "core/java/android/hardware/display/IDisplayManager.aidl",
-        "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
-        "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
-        "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
-        "core/java/android/hardware/face/IFaceService.aidl",
-        "core/java/android/hardware/face/IFaceServiceReceiver.aidl",
-        "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
-        "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
-        "core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
-        "core/java/android/hardware/hdmi/IHdmiControlService.aidl",
-        "core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl",
-        "core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl",
-        "core/java/android/hardware/hdmi/IHdmiInputChangeListener.aidl",
-        "core/java/android/hardware/hdmi/IHdmiMhlVendorCommandListener.aidl",
-        "core/java/android/hardware/hdmi/IHdmiRecordListener.aidl",
-        "core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl",
-        "core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl",
-        "core/java/android/hardware/input/IInputManager.aidl",
-        "core/java/android/hardware/input/IInputDevicesChangedListener.aidl",
-        "core/java/android/hardware/input/ITabletModeChangedListener.aidl",
-        "core/java/android/hardware/iris/IIrisService.aidl",
-        "core/java/android/hardware/location/IActivityRecognitionHardware.aidl",
-        "core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl",
-        "core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl",
-        "core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl",
-        "core/java/android/hardware/location/IGeofenceHardware.aidl",
-        "core/java/android/hardware/location/IGeofenceHardwareCallback.aidl",
-        "core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl",
-        "core/java/android/hardware/location/IContextHubCallback.aidl",
-        "core/java/android/hardware/location/IContextHubClient.aidl",
-        "core/java/android/hardware/location/IContextHubClientCallback.aidl",
-        "core/java/android/hardware/location/IContextHubService.aidl",
-        "core/java/android/hardware/location/IContextHubTransactionCallback.aidl",
-        "core/java/android/hardware/radio/IAnnouncementListener.aidl",
-        "core/java/android/hardware/radio/ICloseHandle.aidl",
-        "core/java/android/hardware/radio/IRadioService.aidl",
-        "core/java/android/hardware/radio/ITuner.aidl",
-        "core/java/android/hardware/radio/ITunerCallback.aidl",
-        "core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
-        "core/java/android/hardware/usb/IUsbManager.aidl",
-        "core/java/android/hardware/usb/IUsbSerialReader.aidl",
-        "core/java/android/net/ICaptivePortal.aidl",
-        "core/java/android/net/IConnectivityManager.aidl",
-        "core/java/android/hardware/ISensorPrivacyListener.aidl",
-        "core/java/android/hardware/ISensorPrivacyManager.aidl",
-        "core/java/android/net/IIpConnectivityMetrics.aidl",
-        "core/java/android/net/IEthernetManager.aidl",
-        "core/java/android/net/IEthernetServiceListener.aidl",
-        "core/java/android/net/INetdEventCallback.aidl",
-        "core/java/android/net/IIpSecService.aidl",
-        "core/java/android/net/INetworkManagementEventObserver.aidl",
-        "core/java/android/net/INetworkPolicyListener.aidl",
-        "core/java/android/net/INetworkPolicyManager.aidl",
-        "core/java/android/net/INetworkRecommendationProvider.aidl",
-        "core/java/android/net/INetworkScoreCache.aidl",
-        "core/java/android/net/INetworkScoreService.aidl",
-        "core/java/android/net/INetworkStatsService.aidl",
-        "core/java/android/net/INetworkStatsSession.aidl",
-        "core/java/android/net/ISocketKeepaliveCallback.aidl",
-        "core/java/android/net/ITestNetworkManager.aidl",
-        "core/java/android/net/ITetheringEventCallback.aidl",
-        "core/java/android/net/ITetheringStatsProvider.aidl",
-        "core/java/android/net/nsd/INsdManager.aidl",
-        "core/java/android/nfc/IAppCallback.aidl",
-        "core/java/android/nfc/INfcAdapter.aidl",
-        "core/java/android/nfc/INfcAdapterExtras.aidl",
-        "core/java/android/nfc/INfcTag.aidl",
-        "core/java/android/nfc/INfcCardEmulation.aidl",
-        "core/java/android/nfc/INfcFCardEmulation.aidl",
-        "core/java/android/nfc/INfcUnlockHandler.aidl",
-        "core/java/android/nfc/INfcDta.aidl",
-        "core/java/android/nfc/ITagRemovedCallback.aidl",
-        "core/java/android/se/omapi/ISecureElementService.aidl",
-        "core/java/android/se/omapi/ISecureElementListener.aidl",
-        "core/java/android/se/omapi/ISecureElementChannel.aidl",
-        "core/java/android/se/omapi/ISecureElementReader.aidl",
-        "core/java/android/se/omapi/ISecureElementSession.aidl",
-        "core/java/android/os/IBatteryPropertiesRegistrar.aidl",
-        "core/java/android/os/ICancellationSignal.aidl",
-        "core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
-        "core/java/android/os/IDeviceIdleController.aidl",
-        "core/java/android/os/IHardwarePropertiesManager.aidl",
-        ":libincident_aidl",
-        "core/java/android/os/IMaintenanceActivityListener.aidl",
-        "core/java/android/os/IMessenger.aidl",
-        "core/java/android/os/INetworkActivityListener.aidl",
-        "core/java/android/os/INetworkManagementService.aidl",
-        "core/java/android/os/IPermissionController.aidl",
-        "core/java/android/os/IProcessInfoService.aidl",
-        "core/java/android/os/IProgressListener.aidl",
-        "core/java/android/os/IPowerManager.aidl",
-        "core/java/android/os/IRecoverySystem.aidl",
-        "core/java/android/os/IRecoverySystemProgressListener.aidl",
-        "core/java/android/os/IRemoteCallback.aidl",
-        "core/java/android/os/ISchedulingPolicyService.aidl",
-        ":statsd_aidl",
-        "core/java/android/os/ISystemUpdateManager.aidl",
-        "core/java/android/os/IThermalEventListener.aidl",
-        "core/java/android/os/IThermalStatusListener.aidl",
-        "core/java/android/os/IThermalService.aidl",
-        "core/java/android/os/IUpdateLock.aidl",
-        "core/java/android/os/IUserManager.aidl",
-        ":libvibrator_aidl",
-        "core/java/android/os/IVibratorService.aidl",
-        "core/java/android/os/image/IDynamicSystemService.aidl",
-        "core/java/android/os/storage/IStorageManager.aidl",
-        "core/java/android/os/storage/IStorageEventListener.aidl",
-        "core/java/android/os/storage/IStorageShutdownObserver.aidl",
-        "core/java/android/os/storage/IObbActionListener.aidl",
-        "core/java/android/permission/IOnPermissionsChangeListener.aidl",
-        "core/java/android/permission/IPermissionController.aidl",
-        "core/java/android/permission/IPermissionManager.aidl",
-        ":keystore_aidl",
-        "core/java/android/security/keymaster/IKeyAttestationApplicationIdProvider.aidl",
-        "core/java/android/service/appprediction/IPredictionService.aidl",
-        "core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl",
-        "core/java/android/service/autofill/augmented/IFillCallback.aidl",
-        "core/java/android/service/autofill/IAutoFillService.aidl",
-        "core/java/android/service/autofill/IAutofillFieldClassificationService.aidl",
-        "core/java/android/service/autofill/IFillCallback.aidl",
-        "core/java/android/service/autofill/ISaveCallback.aidl",
-        "core/java/android/service/carrier/ICarrierService.aidl",
-        "core/java/android/service/carrier/ICarrierMessagingCallback.aidl",
-        "core/java/android/service/carrier/ICarrierMessagingService.aidl",
-        "core/java/android/service/carrier/ICarrierMessagingClientService.aidl",
-        "core/java/android/service/contentsuggestions/IContentSuggestionsService.aidl",
-        "core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl",
-        "core/java/android/service/euicc/IDownloadSubscriptionCallback.aidl",
-        "core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl",
-        "core/java/android/service/euicc/IEuiccService.aidl",
-        "core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl",
-        "core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl",
-        "core/java/android/service/euicc/IGetEidCallback.aidl",
-        "core/java/android/service/euicc/IGetEuiccInfoCallback.aidl",
-        "core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl",
-        "core/java/android/service/euicc/IGetOtaStatusCallback.aidl",
-        "core/java/android/service/euicc/IOtaStatusChangedCallback.aidl",
-        "core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl",
-        "core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl",
-        "core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl",
-        ":gatekeeper_aidl",
-        "core/java/android/service/contentcapture/IContentCaptureService.aidl",
-        "core/java/android/service/contentcapture/IContentCaptureServiceCallback.aidl",
-        "core/java/android/service/notification/INotificationListener.aidl",
-        "core/java/android/service/notification/IStatusBarNotificationHolder.aidl",
-        "core/java/android/service/notification/IConditionListener.aidl",
-        "core/java/android/service/notification/IConditionProvider.aidl",
-        "core/java/android/service/settings/suggestions/ISuggestionService.aidl",
-        "core/java/android/service/sms/IFinancialSmsService.aidl",
-        "core/java/android/service/vr/IPersistentVrStateCallbacks.aidl",
-        "core/java/android/service/vr/IVrListener.aidl",
-        "core/java/android/service/vr/IVrManager.aidl",
-        "core/java/android/service/vr/IVrStateCallbacks.aidl",
-        "core/java/android/service/watchdog/IExplicitHealthCheckService.aidl",
-        "core/java/android/service/watchdog/PackageConfig.aidl",
-        "core/java/android/print/ILayoutResultCallback.aidl",
-        "core/java/android/print/IPrinterDiscoveryObserver.aidl",
-        "core/java/android/print/IPrintDocumentAdapter.aidl",
-        "core/java/android/print/IPrintDocumentAdapterObserver.aidl",
-        "core/java/android/print/IPrintJobStateChangeListener.aidl",
-        "core/java/android/print/IPrintServicesChangeListener.aidl",
-        "core/java/android/printservice/recommendation/IRecommendationsChangeListener.aidl",
-        "core/java/android/print/IPrintManager.aidl",
-        "core/java/android/print/IPrintSpooler.aidl",
-        "core/java/android/print/IPrintSpoolerCallbacks.aidl",
-        "core/java/android/print/IPrintSpoolerClient.aidl",
-        "core/java/android/printservice/recommendation/IRecommendationServiceCallbacks.aidl",
-        "core/java/android/printservice/recommendation/IRecommendationService.aidl",
-        "core/java/android/print/IWriteResultCallback.aidl",
-        "core/java/android/printservice/IPrintService.aidl",
-        "core/java/android/printservice/IPrintServiceClient.aidl",
-        "core/java/android/companion/ICompanionDeviceManager.aidl",
-        "core/java/android/companion/ICompanionDeviceDiscoveryService.aidl",
-        "core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl",
-        "core/java/android/companion/IFindDeviceCallback.aidl",
-        "core/java/android/service/dreams/IDreamManager.aidl",
-        "core/java/android/service/dreams/IDreamService.aidl",
-        "core/java/android/service/oemlock/IOemLockService.aidl",
-        "core/java/android/service/persistentdata/IPersistentDataBlockService.aidl",
-        "core/java/android/service/trust/ITrustAgentService.aidl",
-        "core/java/android/service/trust/ITrustAgentServiceCallback.aidl",
-        "core/java/android/service/voice/IVoiceInteractionService.aidl",
-        "core/java/android/service/voice/IVoiceInteractionSession.aidl",
-        "core/java/android/service/voice/IVoiceInteractionSessionService.aidl",
-        "core/java/android/service/wallpaper/IWallpaperConnection.aidl",
-        "core/java/android/service/wallpaper/IWallpaperEngine.aidl",
-        "core/java/android/service/wallpaper/IWallpaperService.aidl",
-        "core/java/android/service/chooser/IChooserTargetService.aidl",
-        "core/java/android/service/chooser/IChooserTargetResult.aidl",
-        "core/java/android/service/resolver/IResolverRankerService.aidl",
-        "core/java/android/service/resolver/IResolverRankerResult.aidl",
-        "core/java/android/service/textclassifier/ITextClassifierCallback.aidl",
-        "core/java/android/service/textclassifier/ITextClassifierService.aidl",
-        "core/java/android/service/attention/IAttentionService.aidl",
-        "core/java/android/service/attention/IAttentionCallback.aidl",
-        "core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl",
-        "core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl",
-        "core/java/android/view/accessibility/IAccessibilityManager.aidl",
-        "core/java/android/view/accessibility/IAccessibilityManagerClient.aidl",
-        "core/java/android/view/autofill/IAutoFillManager.aidl",
-        "core/java/android/view/autofill/IAutoFillManagerClient.aidl",
-        "core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl",
-        "core/java/android/view/autofill/IAutofillWindowPresenter.aidl",
-        "core/java/android/view/contentcapture/IContentCaptureDirectManager.aidl",
-        "core/java/android/view/contentcapture/IContentCaptureManager.aidl",
-        "core/java/android/view/IApplicationToken.aidl",
-        "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl",
-        "core/java/android/view/IDockedStackListener.aidl",
-        "core/java/android/view/IDisplayFoldListener.aidl",
-        "core/java/android/view/IGraphicsStats.aidl",
-        "core/java/android/view/IGraphicsStatsCallback.aidl",
-        "core/java/android/view/IInputMonitorHost.aidl",
-        "core/java/android/view/IInputFilter.aidl",
-        "core/java/android/view/IInputFilterHost.aidl",
-        "core/java/android/view/IOnKeyguardExitResult.aidl",
-        "core/java/android/view/IPinnedStackController.aidl",
-        "core/java/android/view/IPinnedStackListener.aidl",
-        "core/java/android/view/IRemoteAnimationRunner.aidl",
-        "core/java/android/view/IRecentsAnimationController.aidl",
-        "core/java/android/view/IRecentsAnimationRunner.aidl",
-        "core/java/android/view/IRemoteAnimationFinishedCallback.aidl",
-        "core/java/android/view/IRotationWatcher.aidl",
-        "core/java/android/view/ISystemGestureExclusionListener.aidl",
-        "core/java/android/view/IWallpaperVisibilityListener.aidl",
-        "core/java/android/view/IWindow.aidl",
-        "core/java/android/view/IWindowFocusObserver.aidl",
-        "core/java/android/view/IWindowId.aidl",
-        "core/java/android/view/IWindowManager.aidl",
-        "core/java/android/view/IWindowSession.aidl",
-        "core/java/android/view/IWindowSessionCallback.aidl",
-        "core/java/android/webkit/IWebViewUpdateService.aidl",
-        "core/java/android/speech/IRecognitionListener.aidl",
-        "core/java/android/speech/IRecognitionService.aidl",
-        "core/java/android/speech/tts/ITextToSpeechCallback.aidl",
-        "core/java/android/speech/tts/ITextToSpeechService.aidl",
-        "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
-        "core/java/com/android/internal/app/IAppOpsCallback.aidl",
-        "core/java/com/android/internal/app/IAppOpsNotedCallback.aidl",
-        "core/java/com/android/internal/app/IAppOpsService.aidl",
-        "core/java/com/android/internal/app/IBatteryStats.aidl",
-        "core/java/com/android/internal/app/ISoundTriggerService.aidl",
-        "core/java/com/android/internal/app/IVoiceActionCheckCallback.aidl",
-        "core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl",
-        "core/java/com/android/internal/app/IVoiceInteractionSessionListener.aidl",
-        "core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl",
-        "core/java/com/android/internal/app/IVoiceInteractor.aidl",
-        "core/java/com/android/internal/app/IVoiceInteractorCallback.aidl",
-        "core/java/com/android/internal/app/IVoiceInteractorRequest.aidl",
-        "core/java/com/android/internal/app/IMediaContainerService.aidl",
-        "core/java/com/android/internal/app/procstats/IProcessStats.aidl",
-        "core/java/com/android/internal/appwidget/IAppWidgetService.aidl",
-        "core/java/com/android/internal/appwidget/IAppWidgetHost.aidl",
-        "core/java/com/android/internal/backup/IBackupTransport.aidl",
-        "core/java/com/android/internal/backup/IObbBackupService.aidl",
-        "core/java/com/android/internal/infra/IAndroidFuture.aidl",
-        "core/java/com/android/internal/inputmethod/IInputContentUriToken.aidl",
-        "core/java/com/android/internal/inputmethod/IInputMethodPrivilegedOperations.aidl",
-        "core/java/com/android/internal/inputmethod/IMultiClientInputMethod.aidl",
-        "core/java/com/android/internal/inputmethod/IMultiClientInputMethodPrivilegedOperations.aidl",
-        "core/java/com/android/internal/inputmethod/IMultiClientInputMethodSession.aidl",
-        "core/java/com/android/internal/net/INetworkWatchlistManager.aidl",
-        "core/java/com/android/internal/policy/IKeyguardDrawnCallback.aidl",
-        "core/java/com/android/internal/policy/IKeyguardDismissCallback.aidl",
-        "core/java/com/android/internal/policy/IKeyguardExitCallback.aidl",
-        "core/java/com/android/internal/policy/IKeyguardService.aidl",
-        "core/java/com/android/internal/policy/IKeyguardStateCallback.aidl",
-        "core/java/com/android/internal/policy/IShortcutService.aidl",
-        "core/java/com/android/internal/os/IDropBoxManagerService.aidl",
-        "core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl",
-        "core/java/com/android/internal/os/IResultReceiver.aidl",
-        "core/java/com/android/internal/os/IShellCallback.aidl",
-        "core/java/com/android/internal/statusbar/IStatusBar.aidl",
-        "core/java/com/android/internal/statusbar/IStatusBarService.aidl",
-        "core/java/com/android/internal/statusbar/RegisterStatusBarResult.aidl",
-        "core/java/com/android/internal/textservice/ISpellCheckerService.aidl",
-        "core/java/com/android/internal/textservice/ISpellCheckerServiceCallback.aidl",
-        "core/java/com/android/internal/textservice/ISpellCheckerSession.aidl",
-        "core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl",
-        "core/java/com/android/internal/textservice/ITextServicesManager.aidl",
-        "core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl",
-        "core/java/com/android/internal/view/IDragAndDropPermissions.aidl",
-        "core/java/com/android/internal/view/IInputContext.aidl",
-        "core/java/com/android/internal/view/IInputContextCallback.aidl",
-        "core/java/com/android/internal/view/IInputMethod.aidl",
-        "core/java/com/android/internal/view/IInputMethodClient.aidl",
-        "core/java/com/android/internal/view/IInputMethodManager.aidl",
-        "core/java/com/android/internal/view/IInputMethodSession.aidl",
-        "core/java/com/android/internal/view/IInputSessionCallback.aidl",
-        "core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl",
-        "core/java/com/android/internal/widget/ILockSettings.aidl",
-        "core/java/com/android/internal/widget/IRemoteViewsFactory.aidl",
-        "keystore/java/android/security/IKeyChainAliasCallback.aidl",
-        "keystore/java/android/security/IKeyChainService.aidl",
-        "location/java/android/location/IBatchedLocationCallback.aidl",
-        "location/java/android/location/ICountryDetector.aidl",
-        "location/java/android/location/ICountryListener.aidl",
-        "location/java/android/location/IGeocodeProvider.aidl",
-        "location/java/android/location/IGeofenceProvider.aidl",
-        "location/java/android/location/IGnssStatusListener.aidl",
-        "location/java/android/location/IGnssMeasurementsListener.aidl",
-        "location/java/android/location/IGnssNavigationMessageListener.aidl",
-        "location/java/android/location/ILocationListener.aidl",
-        "location/java/android/location/ILocationManager.aidl",
-        "location/java/android/location/IFusedGeofenceHardware.aidl",
-        "location/java/android/location/IGpsGeofenceHardware.aidl",
-        "location/java/android/location/INetInitiatedListener.aidl",
-        "location/java/com/android/internal/location/ILocationProvider.aidl",
-        "location/java/com/android/internal/location/ILocationProviderManager.aidl",
-        "media/java/android/media/IAudioFocusDispatcher.aidl",
-        "media/java/android/media/IAudioRoutesObserver.aidl",
-        "media/java/android/media/IAudioService.aidl",
-        "media/java/android/media/IAudioServerStateDispatcher.aidl",
-        "media/java/android/media/IMediaHTTPConnection.aidl",
-        "media/java/android/media/IMediaHTTPService.aidl",
-        "media/java/android/media/IMediaResourceMonitor.aidl",
-        "media/java/android/media/IMediaRoute2Provider.aidl",
-        "media/java/android/media/IMediaRoute2ProviderClient.aidl",
-        "media/java/android/media/IMediaRouter2Client.aidl",
-        "media/java/android/media/IMediaRouter2Manager.aidl",
-        "media/java/android/media/IMediaRouterClient.aidl",
-        "media/java/android/media/IMediaRouterService.aidl",
-        "media/java/android/media/IMediaScannerListener.aidl",
-        "media/java/android/media/IMediaScannerService.aidl",
-        "media/java/android/media/IPlaybackConfigDispatcher.aidl",
-        ":libaudioclient_aidl",
-        "media/java/android/media/IRecordingConfigDispatcher.aidl",
-        "media/java/android/media/IRemoteDisplayCallback.aidl",
-        "media/java/android/media/IRemoteDisplayProvider.aidl",
-        "media/java/android/media/IRemoteVolumeController.aidl",
-        "media/java/android/media/IRemoteVolumeObserver.aidl",
-        "media/java/android/media/IRingtonePlayer.aidl",
-        "media/java/android/media/IVolumeController.aidl",
-        "media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl",
-        "media/java/android/media/midi/IBluetoothMidiService.aidl",
-        "media/java/android/media/midi/IMidiDeviceListener.aidl",
-        "media/java/android/media/midi/IMidiDeviceOpenCallback.aidl",
-        "media/java/android/media/midi/IMidiDeviceServer.aidl",
-        "media/java/android/media/midi/IMidiManager.aidl",
-        "media/java/android/media/projection/IMediaProjection.aidl",
-        "media/java/android/media/projection/IMediaProjectionCallback.aidl",
-        "media/java/android/media/projection/IMediaProjectionManager.aidl",
-        "media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl",
-        "media/java/android/media/session/IActiveSessionsListener.aidl",
-        "media/java/android/media/session/ICallback.aidl",
-        "media/java/android/media/session/IOnMediaKeyListener.aidl",
-        "media/java/android/media/session/IOnVolumeKeyLongPressListener.aidl",
-        "media/java/android/media/session/ISession.aidl",
-        "media/java/android/media/session/ISession2TokensListener.aidl",
-        "media/java/android/media/session/ISessionCallback.aidl",
-        "media/java/android/media/session/ISessionController.aidl",
-        "media/java/android/media/session/ISessionControllerCallback.aidl",
-        "media/java/android/media/session/ISessionManager.aidl",
-        "media/java/android/media/soundtrigger/ISoundTriggerDetectionService.aidl",
-        "media/java/android/media/soundtrigger/ISoundTriggerDetectionServiceClient.aidl",
-        "media/java/android/media/tv/ITvInputClient.aidl",
-        "media/java/android/media/tv/ITvInputHardware.aidl",
-        "media/java/android/media/tv/ITvInputHardwareCallback.aidl",
-        "media/java/android/media/tv/ITvInputManager.aidl",
-        "media/java/android/media/tv/ITvInputManagerCallback.aidl",
-        "media/java/android/media/tv/ITvInputService.aidl",
-        "media/java/android/media/tv/ITvInputServiceCallback.aidl",
-        "media/java/android/media/tv/ITvInputSession.aidl",
-        "media/java/android/media/tv/ITvInputSessionCallback.aidl",
-        "media/java/android/media/tv/ITvRemoteProvider.aidl",
-        "media/java/android/media/tv/ITvRemoteServiceInput.aidl",
-        "media/java/android/service/media/IMediaBrowserService.aidl",
-        "media/java/android/service/media/IMediaBrowserServiceCallbacks.aidl",
-        "telecomm/java/com/android/internal/telecom/ICallRedirectionAdapter.aidl",
-        "telecomm/java/com/android/internal/telecom/ICallRedirectionService.aidl",
-        "telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl",
-        "telecomm/java/com/android/internal/telecom/ICallScreeningService.aidl",
-        "telecomm/java/com/android/internal/telecom/IVideoCallback.aidl",
-        "telecomm/java/com/android/internal/telecom/IVideoProvider.aidl",
-        "telecomm/java/com/android/internal/telecom/IConnectionService.aidl",
-        "telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl",
-        "telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl",
-        "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
-        "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionCallback.aidl",
-        "telecomm/java/com/android/internal/telecom/IPhoneAccountSuggestionService.aidl",
-        "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
-        "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
-        "telephony/java/android/telephony/data/IDataService.aidl",
-        "telephony/java/android/telephony/data/IDataServiceCallback.aidl",
-        "telephony/java/android/telephony/data/IQualifiedNetworksService.aidl",
-        "telephony/java/android/telephony/data/IQualifiedNetworksServiceCallback.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsCapabilityCallback.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsConfig.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsConfigCallback.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsMmTelFeature.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsMmTelListener.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsRegistration.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsRegistrationCallback.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
-        "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
-        "telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl",
-        "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
-        "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
-        "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
-        "telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
-        "telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl",
-        "telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl",
-        "telephony/java/android/telephony/mbms/IGroupCallCallback.aidl",
-        "telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl",
-        "telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl",
-        "telephony/java/android/telephony/mbms/vendor/IMbmsGroupCallService.aidl",
-        "telephony/java/android/telephony/ICellInfoCallback.aidl",
-        "telephony/java/android/telephony/IFinancialSmsCallback.aidl",
-        "telephony/java/android/telephony/INetworkService.aidl",
-        "telephony/java/android/telephony/INetworkServiceCallback.aidl",
-        "telephony/java/com/android/ims/internal/IImsCallSession.aidl",
-        "telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl",
-        "telephony/java/com/android/ims/internal/IImsConfig.aidl",
-        "telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl",
-        "telephony/java/com/android/ims/internal/IImsEcbm.aidl",
-        "telephony/java/com/android/ims/internal/IImsEcbmListener.aidl",
-        "telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl",
-        "telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl",
-        "telephony/java/com/android/ims/internal/IImsMMTelFeature.aidl",
-        "telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl",
-        "telephony/java/com/android/ims/internal/IImsRcsFeature.aidl",
-        "telephony/java/com/android/ims/internal/IImsService.aidl",
-        "telephony/java/com/android/ims/internal/IImsServiceController.aidl",
-        "telephony/java/com/android/ims/internal/IImsServiceFeatureCallback.aidl",
-        "telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl",
-        "telephony/java/com/android/ims/internal/IImsUt.aidl",
-        "telephony/java/com/android/ims/internal/IImsUtListener.aidl",
-        "telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl",
-        "telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl",
-        "telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl",
-        "telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl",
-        "telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl",
-        "telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl",
-        "telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl",
-        "telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl",
-        "telephony/java/com/android/ims/ImsConfigListener.aidl",
-        "telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
-        "telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
-        "telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl",
-        "telephony/java/com/android/internal/telephony/IMms.aidl",
-        "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
-        "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
-        "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
-        "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
-        "telephony/java/com/android/internal/telephony/ISetOpportunisticDataCallback.aidl",
-        "telephony/java/com/android/internal/telephony/ISms.aidl",
-        "telephony/java/com/android/internal/telephony/ISub.aidl",
-        "telephony/java/com/android/internal/telephony/IOns.aidl",
-        "telephony/java/com/android/internal/telephony/ITelephony.aidl",
-        "telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl",
-        "telephony/java/com/android/internal/telephony/IUpdateAvailableNetworksCallback.aidl",
-        "telephony/java/com/android/internal/telephony/IWapPushManager.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IAuthenticateServerCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/ICancelSessionCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IDeleteProfileCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IDisableProfileCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetAllProfilesCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetDefaultSmdpAddressCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetEuiccChallengeCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo1Callback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetEuiccInfo2Callback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetProfileCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetRulesAuthTableCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IGetSmdsAddressCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IListNotificationsCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/ILoadBoundProfilePackageCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IPrepareDownloadCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IRemoveNotificationFromListCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IResetMemoryCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/IRetrieveNotificationListCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
-        "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
-        "wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl",
-        "wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl",
-        "wifi/java/android/net/wifi/ISoftApCallback.aidl",
-        "wifi/java/android/net/wifi/ITrafficStateCallback.aidl",
-        "wifi/java/android/net/wifi/IWifiManager.aidl",
-        "wifi/java/android/net/wifi/IOnWifiUsabilityStatsListener.aidl",
-        "wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
-        "wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
-        "wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl",
-        "wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl",
-        "wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl",
-        "wifi/java/android/net/wifi/rtt/IRttCallback.aidl",
-        "wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl",
-        "wifi/java/android/net/wifi/hotspot2/IProvisioningCallback.aidl",
-        "wifi/java/android/net/wifi/IDppCallback.aidl",
-        "wifi/java/android/net/wifi/IWifiScanner.aidl",
-        "packages/services/PacProcessor/com/android/net/IProxyService.aidl",
-        "packages/services/Proxy/com/android/net/IProxyCallback.aidl",
-        "packages/services/Proxy/com/android/net/IProxyPortListener.aidl",
-        "core/java/android/service/quicksettings/IQSService.aidl",
-        "core/java/android/service/quicksettings/IQSTileService.aidl",
-
-        ":libupdate_engine_aidl",
-
-        ":storaged_aidl",
-        ":vold_aidl",
-        ":gsiservice_aidl",
-        ":installd_aidl",
-        ":dumpstate_aidl",
-        ":incidentcompanion_aidl",
-
-        "lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl",
-        "lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl",
-        "lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl",
-        "lowpan/java/android/net/lowpan/ILowpanInterface.aidl",
-        "lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl",
-        "lowpan/java/android/net/lowpan/ILowpanManager.aidl",
-
-        "core/java/android/app/admin/SecurityLogTags.logtags",
-        "core/java/android/content/EventLogTags.logtags",
-        "core/java/android/speech/tts/EventLogTags.logtags",
-        "core/java/android/net/EventLogTags.logtags",
-        "core/java/android/os/EventLogTags.logtags",
-        "core/java/android/webkit/EventLogTags.logtags",
-        "core/java/com/android/internal/app/EventLogTags.logtags",
-        "core/java/com/android/internal/logging/EventLogTags.logtags",
-        "core/java/com/android/server/DropboxLogTags.logtags",
-        "core/java/org/chromium/arc/EventLogTags.logtags",
-
-        ":apex-properties",
-        ":platform-properties",
-
-        ":framework-statslog-gen",
-    ],
+        ":framework-defaults-java-srcs",
+    ] + framework_srcs,
 
     aidl: {
-        include_dirs: [
-            "system/update_engine/binder_bindings",
-            "frameworks/native/aidl/binder",
-            "frameworks/native/cmds/dumpstate/binder",
-            "frameworks/native/libs/incidentcompanion/binder",
-            "frameworks/av/camera/aidl",
-            "frameworks/av/media/libaudioclient/aidl",
-            "frameworks/native/aidl/gui",
-            "frameworks/native/libs/incidentcompanion/binder",
-            "system/core/gatekeeperd/binder",
-            "system/core/storaged/binder",
-            "system/vold/binder",
-            "system/gsid/aidl",
-            "system/bt/binder",
-            "system/security/keystore/binder",
-        ],
-
+        local_include_dirs: framework_aidl_local_include_dirs,
+        include_dirs: framework_aidl_external_include_dirs,
         generate_get_transaction_name: true,
     },
 
@@ -790,6 +190,9 @@
         "android.hardware.vibrator-V1.3-java",
         "android.hardware.wifi-V1.0-java-constants",
         "devicepolicyprotosnano",
+
+        "com.android.sysprop.apex",
+        "PlatformProperties",
     ],
 
     required: [
@@ -845,10 +248,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     javac_shard_size: 150,
-    required: [
-        "framework-platform-compat-config",
-        "libcore-platform-compat-config",
-    ],
 }
 
 java_library {
@@ -859,6 +258,10 @@
         "framework-minus-apex",
         "jobscheduler-framework",
     ],
+    required: [
+        "framework-platform-compat-config",
+        "libcore-platform-compat-config",
+    ],
     sdk_version: "core_platform",
 }
 
@@ -1769,10 +1172,10 @@
     name: "hiddenapi-mappings",
     defaults: ["metalava-api-stubs-default"],
     srcs: [
-        ":openjdk_java_files",
+        ":framework-defaults-java-srcs",
         ":non_openjdk_java_files",
+        ":openjdk_java_files",
         ":opt-telephony-common-srcs",
-        "core/java/**/*.java",
     ],
     arg_files: [
         "core/res/AndroidManifest.xml",
@@ -1827,6 +1230,7 @@
         last_released: {
             api_file: ":last-released-public-api",
             removed_api_file: "api/removed.txt",
+            baseline_file: ":public-api-incompatibilities-with-last-released",
         },
     },
     jdiff_enabled: true,
@@ -1852,6 +1256,7 @@
         last_released: {
             api_file: ":last-released-system-api",
             removed_api_file: "api/system-removed.txt",
+            baseline_file: ":system-api-incompatibilities-with-last-released"
         },
     },
     jdiff_enabled: true,
@@ -1906,7 +1311,9 @@
 // annotations to private apis
 aidl_mapping {
     name: "framework-aidl-mappings",
-    srcs: [":framework-defaults"],
+    srcs: framework_srcs,
+    local_include_dirs: framework_aidl_local_include_dirs,
+    include_dirs: framework_aidl_external_include_dirs,
     output: "framework-aidl-mappings.txt",
 }
 
diff --git a/apex/jobscheduler/OWNERS b/apex/jobscheduler/OWNERS
new file mode 100644
index 0000000..d004eed
--- /dev/null
+++ b/apex/jobscheduler/OWNERS
@@ -0,0 +1,6 @@
+yamasani@google.com
+omakoto@google.com
+ctate@android.com
+ctate@google.com
+kwekua@google.com
+suprabh@google.com
\ No newline at end of file
diff --git a/apex/jobscheduler/README_js-mainline.md b/apex/jobscheduler/README_js-mainline.md
index b5fea5e..c1ad666 100644
--- a/apex/jobscheduler/README_js-mainline.md
+++ b/apex/jobscheduler/README_js-mainline.md
@@ -6,46 +6,33 @@
 - http://go/moving-js-code-for-mainline
 - http://go/jobscheduler-code-dependencies-2019-07
 
-- [ ] Move client code
-  - [ ] Move code
-  - [ ] Make build file
-  - [ ] "m jobscheduler-framework" pass
-  - [ ] "m framework" pass
-  - [ ] "m service" pass
-- [ ] Move proto
-  - No, couldn't do it, because it's referred to by incidentd_proto
-- [ ] Move service
-  - [X] Move code (done, but it won't compile yet)
-  - [X] Make build file
-  - [X] "m service" pass
-  - [X] "m jobscheduler-service" pass
-    - To make it pass, jobscheduler-service has to link services.jar too. Many dependencies.
 - [ ] Move this into `frameworks/apex/jobscheduler/...`. Currently it's in `frameworks/base/apex/...`
 because `frameworks/apex/` is not a part of any git projects. (and also working on multiple
 projects is a pain.)
 
+## Current structure
 
-## Problems
-- Couldn't move dumpsys proto files. They are used by incidentd_proto, which is in the platform
-  (not updatable).
-  - One idea is *not* to move the proto files into apex but keep them in the platform.
-    Then we make sure to extend the proto files in a backward-compat way (which we do anyway)
-    and always use the latest file from the JS apex.
+- JS service side classes are put in `jobscheduler-service.jar`.
+It's *not* included in services.jar, and instead it's put in the system server classpath,
+which currently looks like the following:
+`SYSTEMSERVERCLASSPATH=/system/framework/services.jar:/system/framework/jobscheduler-service.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar:/system/framework/com.android.location.provider.jar`
 
-- There are a lot of build tasks that use "framework.jar". (Examples: hiddenapi-greylist.txt check,
-  update-api / public API check and SDK stub (android.jar) creation)
-  To make the downstream build modules buildable, we need to include js-framework.jar in
-  framework.jar. However it turned out to be tricky because soong has special logic for "framework"
-  and "framework.jar".
-  i.e. Conceptually, we can do it by renaming `framework` to `framework-minus-jobscheduler`, build
-  `jobscheduler-framework` with `framework-minus-jobscheduler`, and create `framework` by merging
-  `framework-minus-jobscheduler` and `jobscheduler-framework`.
-  However it didn't quite work because of the special casing.
+  (Note `jobscheduler-service.jar` will be put at the end in http://ag/9128109)
 
-- JS-service uses a lot of other code in `services`, so it needs to link services.core.jar e.g.
- - Common system service code, e.g. `com.android.server.SystemService`
- - Common utility code, e.g. `FgThread` and `IoThread`
- - Other system services such as `DeviceIdleController` and `ActivityManagerService`
- - Server side singleton. `AppStateTracker`
- - `DeviceIdleController.LocalService`, which is a local service but there's no interface class.
- - `XxxInternal` interfaces that are not in the framework side. -> We should be able to move them.
+  `SYSTEMSERVERCLASSPATH` is generated from `PRODUCT_SYSTEM_SERVER_JARS`.
+
+- JS framework side classes are put in `jobscheduler-framework.jar`,
+and the rest of the framework code is put in `framework-minus-apex.jar`,
+as of http://ag/9145619.
+
+  However these jar files are *not* put on the device. We still generate
+  `framework.jar` merging the two jar files, and this jar file is what's
+  put on the device and loaded by Zygote.
+
+
+This is *not* the final design. From a gerrit comment on http://ag/9145619:
+
+> This CL is just the first step, and the current state isn't not really the final form. For now we just want to have two separate jars, which makes it easier for us to analyze dependencies between them, and I wanted to minimize the change to the rest of the system. So, for example, zygote will still only have "framework.jar" in its classpath, instead of the two jars for now.
+> But yes, eventually, we won't even be able to have the monolithic "framework.jar" file because of mainline, so we need to figure out how to build the system without creating it. At that point zygote will have the two separate jar files in its classpath.
+> When we reach that point, we should revisit the naming of it, and yes, maybe the simple "framework.jar" is a good option.
+> But again, for now, I want to make this change as transparent as possible to the rest of the world.
diff --git a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
index 005b189..b7e8cf6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
@@ -16,7 +16,6 @@
 
 package com.android.server.job;
 
-import android.app.IActivityManager;
 import android.app.UriGrantsManager;
 import android.content.ClipData;
 import android.content.ContentProvider;
@@ -40,7 +39,7 @@
     private final IBinder mPermissionOwner;
     private final ArrayList<Uri> mUris = new ArrayList<>();
 
-    private GrantedUriPermissions(IActivityManager am, int grantFlags, int uid, String tag)
+    private GrantedUriPermissions(int grantFlags, int uid, String tag)
             throws RemoteException {
         mGrantFlags = grantFlags;
         mSourceUserId = UserHandle.getUserId(uid);
@@ -49,7 +48,7 @@
                 .getService(UriGrantsManagerInternal.class).newUriPermissionOwner("job: " + tag);
     }
 
-    public void revoke(IActivityManager am) {
+    public void revoke() {
         for (int i = mUris.size()-1; i >= 0; i--) {
             LocalServices.getService(UriGrantsManagerInternal.class).revokeUriPermissionFromOwner(
                     mPermissionOwner, mUris.get(i), mGrantFlags, mSourceUserId);
@@ -62,7 +61,7 @@
                 |Intent.FLAG_GRANT_READ_URI_PERMISSION)) != 0;
     }
 
-    public static GrantedUriPermissions createFromIntent(IActivityManager am, Intent intent,
+    public static GrantedUriPermissions createFromIntent(Intent intent,
             int sourceUid, String targetPackage, int targetUserId, String tag) {
         int grantFlags = intent.getFlags();
         if (!checkGrantFlags(grantFlags)) {
@@ -73,44 +72,44 @@
 
         Uri data = intent.getData();
         if (data != null) {
-            perms = grantUri(am, data, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+            perms = grantUri(data, sourceUid, targetPackage, targetUserId, grantFlags, tag,
                     perms);
         }
 
         ClipData clip = intent.getClipData();
         if (clip != null) {
-            perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+            perms = grantClip(clip, sourceUid, targetPackage, targetUserId, grantFlags, tag,
                     perms);
         }
 
         return perms;
     }
 
-    public static GrantedUriPermissions createFromClip(IActivityManager am, ClipData clip,
+    public static GrantedUriPermissions createFromClip(ClipData clip,
             int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag) {
         if (!checkGrantFlags(grantFlags)) {
             return null;
         }
         GrantedUriPermissions perms = null;
         if (clip != null) {
-            perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags,
+            perms = grantClip(clip, sourceUid, targetPackage, targetUserId, grantFlags,
                     tag, perms);
         }
         return perms;
     }
 
-    private static GrantedUriPermissions grantClip(IActivityManager am, ClipData clip,
+    private static GrantedUriPermissions grantClip(ClipData clip,
             int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
             GrantedUriPermissions curPerms) {
         final int N = clip.getItemCount();
         for (int i = 0; i < N; i++) {
-            curPerms = grantItem(am, clip.getItemAt(i), sourceUid, targetPackage, targetUserId,
+            curPerms = grantItem(clip.getItemAt(i), sourceUid, targetPackage, targetUserId,
                     grantFlags, tag, curPerms);
         }
         return curPerms;
     }
 
-    private static GrantedUriPermissions grantUri(IActivityManager am, Uri uri,
+    private static GrantedUriPermissions grantUri(Uri uri,
             int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
             GrantedUriPermissions curPerms) {
         try {
@@ -118,7 +117,7 @@
                     UserHandle.getUserId(sourceUid));
             uri = ContentProvider.getUriWithoutUserId(uri);
             if (curPerms == null) {
-                curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag);
+                curPerms = new GrantedUriPermissions(grantFlags, sourceUid, tag);
             }
             UriGrantsManager.getService().grantUriPermissionFromOwner(curPerms.mPermissionOwner,
                     sourceUid, targetPackage, uri, grantFlags, sourceUserId, targetUserId);
@@ -129,16 +128,16 @@
         return curPerms;
     }
 
-    private static GrantedUriPermissions grantItem(IActivityManager am, ClipData.Item item,
+    private static GrantedUriPermissions grantItem(ClipData.Item item,
             int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
             GrantedUriPermissions curPerms) {
         if (item.getUri() != null) {
-            curPerms = grantUri(am, item.getUri(), sourceUid, targetPackage, targetUserId,
+            curPerms = grantUri(item.getUri(), sourceUid, targetPackage, targetUserId,
                     grantFlags, tag, curPerms);
         }
         Intent intent = item.getIntent();
         if (intent != null && intent.getData() != null) {
-            curPerms = grantUri(am, intent.getData(), sourceUid, targetPackage, targetUserId,
+            curPerms = grantUri(intent.getData(), sourceUid, targetPackage, targetUserId,
                     grantFlags, tag, curPerms);
         }
         return curPerms;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 5ba563c..a633350 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -89,7 +89,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.AppStateTracker;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
@@ -207,7 +207,7 @@
     PackageManagerInternal mLocalPM;
     ActivityManagerInternal mActivityManagerInternal;
     IBatteryStats mBatteryStats;
-    DeviceIdleController.LocalService mLocalDeviceIdleController;
+    DeviceIdleInternal mLocalDeviceIdleController;
     AppStateTracker mAppStateTracker;
     final UsageStatsManagerInternal mUsageStats;
 
@@ -963,7 +963,7 @@
                 // changing.  We can just directly enqueue this work in to the job.
                 if (toCancel.getJob().equals(job)) {
 
-                    toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
+                    toCancel.enqueueWorkLocked(work);
 
                     // If any of work item is enqueued when the source is in the foreground,
                     // exempt the entire job.
@@ -992,11 +992,11 @@
             }
 
             // This may throw a SecurityException.
-            jobStatus.prepareLocked(ActivityManager.getService());
+            jobStatus.prepareLocked();
 
             if (work != null) {
                 // If work has been supplied, enqueue it into the new job.
-                jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
+                jobStatus.enqueueWorkLocked(work);
             }
 
             if (toCancel != null) {
@@ -1144,7 +1144,7 @@
      */
     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
-        cancelled.unprepareLocked(ActivityManager.getService());
+        cancelled.unprepareLocked();
         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
         // Remove from pending queue.
         if (mPendingJobs.remove(cancelled)) {
@@ -1399,8 +1399,8 @@
                 mReadyToRock = true;
                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                         BatteryStats.SERVICE_NAME));
-                mLocalDeviceIdleController
-                        = LocalServices.getService(DeviceIdleController.LocalService.class);
+                mLocalDeviceIdleController =
+                        LocalServices.getService(DeviceIdleInternal.class);
                 // Create the "runners".
                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
                     mActiveServices.add(
@@ -1449,7 +1449,7 @@
     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
             boolean removeFromPersisted) {
         // Deal with any remaining work items in the old job.
-        jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
+        jobStatus.stopTrackingJobLocked(incomingJob);
 
         // Remove from store as well as controllers.
         final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
@@ -1705,7 +1705,7 @@
 
         if (rescheduledJob != null) {
             try {
-                rescheduledJob.prepareLocked(ActivityManager.getService());
+                rescheduledJob.prepareLocked();
             } catch (SecurityException e) {
                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
             }
@@ -1713,13 +1713,13 @@
         } else if (jobStatus.getJob().isPeriodic()) {
             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
             try {
-                rescheduledPeriodic.prepareLocked(ActivityManager.getService());
+                rescheduledPeriodic.prepareLocked();
             } catch (SecurityException e) {
                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
             }
             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
         }
-        jobStatus.unprepareLocked(ActivityManager.getService());
+        jobStatus.unprepareLocked();
         reportActiveLocked();
         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 4d9f133..782e646 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -18,7 +18,6 @@
 
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
-import android.app.ActivityManager;
 import android.app.job.IJobCallback;
 import android.app.job.IJobService;
 import android.app.job.JobInfo;
@@ -389,7 +388,7 @@
         try {
             synchronized (mLock) {
                 assertCallerLocked(cb);
-                return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId);
+                return mRunningJob.completeWorkLocked(workId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 4321fc7..c2bdb6c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -20,8 +20,6 @@
 import static com.android.server.job.JobSchedulerService.sSystemClock;
 
 import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
 import android.app.job.JobInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -180,7 +178,6 @@
     public void getRtcCorrectedJobsLocked(final ArrayList<JobStatus> toAdd,
             final ArrayList<JobStatus> toRemove) {
         final long elapsedNow = sElapsedRealtimeClock.millis();
-        final IActivityManager am = ActivityManager.getService();
 
         // Find the jobs that need to be fixed up, collecting them for post-iteration
         // replacement with their new versions
@@ -192,7 +189,7 @@
                 JobStatus newJob = new JobStatus(job,
                         elapsedRuntimes.first, elapsedRuntimes.second,
                         0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
-                newJob.prepareLocked(am);
+                newJob.prepareLocked();
                 toAdd.add(newJob);
                 toRemove.add(job);
             }
@@ -667,10 +664,9 @@
                     jobs = readJobMapImpl(fis, rtcGood);
                     if (jobs != null) {
                         long now = sElapsedRealtimeClock.millis();
-                        IActivityManager am = ActivityManager.getService();
                         for (int i=0; i<jobs.size(); i++) {
                             JobStatus js = jobs.get(i);
-                            js.prepareLocked(am);
+                            js.prepareLocked();
                             js.enqueueTime = now;
                             this.jobSet.add(js);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index 0b67971..01f5fa6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -34,7 +34,7 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateControllerProto;
@@ -66,7 +66,7 @@
     private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor;
     private final DeviceIdleJobsDelayHandler mHandler;
     private final PowerManager mPowerManager;
-    private final DeviceIdleController.LocalService mLocalDeviceIdleController;
+    private final DeviceIdleInternal mLocalDeviceIdleController;
 
     /**
      * True when in device idle mode, so we don't want to schedule any jobs.
@@ -123,7 +123,7 @@
         // Register for device idle mode changes
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mLocalDeviceIdleController =
-                LocalServices.getService(DeviceIdleController.LocalService.class);
+                LocalServices.getService(DeviceIdleInternal.class);
         mDeviceIdleWhitelistAppIds = mLocalDeviceIdleController.getPowerSaveWhitelistUserAppIds();
         mPowerSaveTempWhitelistAppIds =
                 mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 1133f7b..adb4314 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -20,7 +20,6 @@
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import android.app.AppGlobals;
-import android.app.IActivityManager;
 import android.app.job.JobInfo;
 import android.app.job.JobWorkItem;
 import android.content.ClipData;
@@ -528,7 +527,7 @@
                 /*innerFlags=*/ 0);
     }
 
-    public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
+    public void enqueueWorkLocked(JobWorkItem work) {
         if (pendingWork == null) {
             pendingWork = new ArrayList<>();
         }
@@ -536,7 +535,7 @@
         nextPendingWorkId++;
         if (work.getIntent() != null
                 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
-            work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid,
+            work.setGrants(GrantedUriPermissions.createFromIntent(work.getIntent(), sourceUid,
                     sourcePackageName, sourceUserId, toShortString()));
         }
         pendingWork.add(work);
@@ -567,20 +566,20 @@
         return executingWork != null && executingWork.size() > 0;
     }
 
-    private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
+    private static void ungrantWorkItem(JobWorkItem work) {
         if (work.getGrants() != null) {
-            ((GrantedUriPermissions)work.getGrants()).revoke(am);
+            ((GrantedUriPermissions)work.getGrants()).revoke();
         }
     }
 
-    public boolean completeWorkLocked(IActivityManager am, int workId) {
+    public boolean completeWorkLocked(int workId) {
         if (executingWork != null) {
             final int N = executingWork.size();
             for (int i = 0; i < N; i++) {
                 JobWorkItem work = executingWork.get(i);
                 if (work.getWorkId() == workId) {
                     executingWork.remove(i);
-                    ungrantWorkItem(am, work);
+                    ungrantWorkItem(work);
                     return true;
                 }
             }
@@ -588,16 +587,16 @@
         return false;
     }
 
-    private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) {
+    private static void ungrantWorkList(ArrayList<JobWorkItem> list) {
         if (list != null) {
             final int N = list.size();
             for (int i = 0; i < N; i++) {
-                ungrantWorkItem(am, list.get(i));
+                ungrantWorkItem(list.get(i));
             }
         }
     }
 
-    public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) {
+    public void stopTrackingJobLocked(JobStatus incomingJob) {
         if (incomingJob != null) {
             // We are replacing with a new job -- transfer the work!  We do any executing
             // work first, since that was originally at the front of the pending work.
@@ -615,15 +614,15 @@
             incomingJob.updateEstimatedNetworkBytesLocked();
         } else {
             // We are completely stopping the job...  need to clean up work.
-            ungrantWorkList(am, pendingWork);
+            ungrantWorkList(pendingWork);
             pendingWork = null;
-            ungrantWorkList(am, executingWork);
+            ungrantWorkList(executingWork);
             executingWork = null;
         }
         updateEstimatedNetworkBytesLocked();
     }
 
-    public void prepareLocked(IActivityManager am) {
+    public void prepareLocked() {
         if (prepared) {
             Slog.wtf(TAG, "Already prepared: " + this);
             return;
@@ -634,12 +633,12 @@
         }
         final ClipData clip = job.getClipData();
         if (clip != null) {
-            uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName,
+            uriPerms = GrantedUriPermissions.createFromClip(clip, sourceUid, sourcePackageName,
                     sourceUserId, job.getClipGrantFlags(), toShortString());
         }
     }
 
-    public void unprepareLocked(IActivityManager am) {
+    public void unprepareLocked() {
         if (!prepared) {
             Slog.wtf(TAG, "Hasn't been prepared: " + this);
             if (DEBUG_PREPARE && unpreparedPoint != null) {
@@ -652,7 +651,7 @@
             unpreparedPoint = new Throwable().fillInStackTrace();
         }
         if (uriPerms != null) {
-            uriPerms.revoke(am);
+            uriPerms.revoke();
             uriPerms = null;
         }
     }
diff --git a/api/current.txt b/api/current.txt
index e11fca8..593710314 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2826,6 +2826,7 @@
     method public final boolean dispatchGesture(@NonNull android.accessibilityservice.GestureDescription, @Nullable android.accessibilityservice.AccessibilityService.GestureResultCallback, @Nullable android.os.Handler);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method @NonNull public final android.accessibilityservice.AccessibilityButtonController getAccessibilityButtonController();
+    method @NonNull public final android.accessibilityservice.AccessibilityButtonController getAccessibilityButtonController(int);
     method @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) @NonNull public final android.accessibilityservice.FingerprintGestureController getFingerprintGestureController();
     method @NonNull public final android.accessibilityservice.AccessibilityService.MagnificationController getMagnificationController();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
@@ -6726,6 +6727,7 @@
     method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
     method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
     method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
+    method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
     method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
     method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int);
     method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]);
@@ -6772,6 +6774,7 @@
     method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long);
     method @Nullable public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(@NonNull android.content.ComponentName);
     method @Nullable public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(@NonNull android.content.ComponentName);
+    method public boolean revokeKeyPairFromApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
     method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean);
     method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
     method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6795,7 +6798,6 @@
     method @WorkerThread public int setGlobalPrivateDnsModeSpecifiedHost(@NonNull android.content.ComponentName, @NonNull String);
     method public void setGlobalSetting(@NonNull android.content.ComponentName, String, String);
     method public void setKeepUninstalledPackages(@Nullable android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
-    method public boolean setKeyGrantForApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String, boolean);
     method public boolean setKeyPairCertificate(@Nullable android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.security.cert.Certificate>, boolean);
     method public boolean setKeyguardDisabled(@NonNull android.content.ComponentName, boolean);
     method public void setKeyguardDisabledFeatures(@NonNull android.content.ComponentName, int);
@@ -18416,8 +18418,12 @@
     field public static final int EARLY_DYNASTIC_CUNEIFORM_ID = 257; // 0x101
     field public static final android.icu.lang.UCharacter.UnicodeBlock EGYPTIAN_HIEROGLYPHS;
     field public static final int EGYPTIAN_HIEROGLYPHS_ID = 194; // 0xc2
+    field public static final android.icu.lang.UCharacter.UnicodeBlock EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS;
+    field public static final int EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS_ID = 292; // 0x124
     field public static final android.icu.lang.UCharacter.UnicodeBlock ELBASAN;
     field public static final int ELBASAN_ID = 226; // 0xe2
+    field public static final android.icu.lang.UCharacter.UnicodeBlock ELYMAIC;
+    field public static final int ELYMAIC_ID = 293; // 0x125
     field public static final android.icu.lang.UCharacter.UnicodeBlock EMOTICONS;
     field public static final int EMOTICONS_ID = 206; // 0xce
     field public static final android.icu.lang.UCharacter.UnicodeBlock ENCLOSED_ALPHANUMERICS;
@@ -18644,6 +18650,8 @@
     field public static final int MYANMAR_ID = 28; // 0x1c
     field public static final android.icu.lang.UCharacter.UnicodeBlock NABATAEAN;
     field public static final int NABATAEAN_ID = 239; // 0xef
+    field public static final android.icu.lang.UCharacter.UnicodeBlock NANDINAGARI;
+    field public static final int NANDINAGARI_ID = 294; // 0x126
     field public static final android.icu.lang.UCharacter.UnicodeBlock NEWA;
     field public static final int NEWA_ID = 270; // 0x10e
     field public static final android.icu.lang.UCharacter.UnicodeBlock NEW_TAI_LUE;
@@ -18655,6 +18663,8 @@
     field public static final int NUMBER_FORMS_ID = 45; // 0x2d
     field public static final android.icu.lang.UCharacter.UnicodeBlock NUSHU;
     field public static final int NUSHU_ID = 277; // 0x115
+    field public static final android.icu.lang.UCharacter.UnicodeBlock NYIAKENG_PUACHUE_HMONG;
+    field public static final int NYIAKENG_PUACHUE_HMONG_ID = 295; // 0x127
     field public static final android.icu.lang.UCharacter.UnicodeBlock OGHAM;
     field public static final int OGHAM_ID = 34; // 0x22
     field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_HUNGARIAN;
@@ -18685,6 +18695,8 @@
     field public static final int OSAGE_ID = 271; // 0x10f
     field public static final android.icu.lang.UCharacter.UnicodeBlock OSMANYA;
     field public static final int OSMANYA_ID = 122; // 0x7a
+    field public static final android.icu.lang.UCharacter.UnicodeBlock OTTOMAN_SIYAQ_NUMBERS;
+    field public static final int OTTOMAN_SIYAQ_NUMBERS_ID = 296; // 0x128
     field public static final android.icu.lang.UCharacter.UnicodeBlock PAHAWH_HMONG;
     field public static final int PAHAWH_HMONG_ID = 243; // 0xf3
     field public static final android.icu.lang.UCharacter.UnicodeBlock PALMYRENE;
@@ -18733,6 +18745,8 @@
     field public static final int SINHALA_ID = 24; // 0x18
     field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_FORM_VARIANTS;
     field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54
+    field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_KANA_EXTENSION;
+    field public static final int SMALL_KANA_EXTENSION_ID = 297; // 0x129
     field public static final android.icu.lang.UCharacter.UnicodeBlock SOGDIAN;
     field public static final int SOGDIAN_ID = 291; // 0x123
     field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG;
@@ -18769,6 +18783,8 @@
     field public static final int SUTTON_SIGNWRITING_ID = 262; // 0x106
     field public static final android.icu.lang.UCharacter.UnicodeBlock SYLOTI_NAGRI;
     field public static final int SYLOTI_NAGRI_ID = 143; // 0x8f
+    field public static final android.icu.lang.UCharacter.UnicodeBlock SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A;
+    field public static final int SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A_ID = 298; // 0x12a
     field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC;
     field public static final int SYRIAC_ID = 13; // 0xd
     field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC_SUPPLEMENT;
@@ -18791,6 +18807,8 @@
     field public static final int TAKRI_ID = 220; // 0xdc
     field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL;
     field public static final int TAMIL_ID = 20; // 0x14
+    field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL_SUPPLEMENT;
+    field public static final int TAMIL_SUPPLEMENT_ID = 299; // 0x12b
     field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT;
     field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS;
     field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111
@@ -18825,6 +18843,8 @@
     field public static final int VEDIC_EXTENSIONS_ID = 175; // 0xaf
     field public static final android.icu.lang.UCharacter.UnicodeBlock VERTICAL_FORMS;
     field public static final int VERTICAL_FORMS_ID = 145; // 0x91
+    field public static final android.icu.lang.UCharacter.UnicodeBlock WANCHO;
+    field public static final int WANCHO_ID = 300; // 0x12c
     field public static final android.icu.lang.UCharacter.UnicodeBlock WARANG_CITI;
     field public static final int WARANG_CITI_ID = 252; // 0xfc
     field public static final android.icu.lang.UCharacter.UnicodeBlock YIJING_HEXAGRAM_SYMBOLS;
@@ -19137,6 +19157,7 @@
     field public static final int EASTERN_SYRIAC = 97; // 0x61
     field public static final int EGYPTIAN_HIEROGLYPHS = 71; // 0x47
     field public static final int ELBASAN = 136; // 0x88
+    field public static final int ELYMAIC = 185; // 0xb9
     field public static final int ESTRANGELO_SYRIAC = 95; // 0x5f
     field public static final int ETHIOPIC = 11; // 0xb
     field public static final int GEORGIAN = 12; // 0xc
@@ -19216,10 +19237,12 @@
     field public static final int MYANMAR = 28; // 0x1c
     field public static final int NABATAEAN = 143; // 0x8f
     field public static final int NAKHI_GEBA = 132; // 0x84
+    field public static final int NANDINAGARI = 187; // 0xbb
     field public static final int NEWA = 170; // 0xaa
     field public static final int NEW_TAI_LUE = 59; // 0x3b
     field public static final int NKO = 87; // 0x57
     field public static final int NUSHU = 150; // 0x96
+    field public static final int NYIAKENG_PUACHUE_HMONG = 186; // 0xba
     field public static final int OGHAM = 29; // 0x1d
     field public static final int OLD_CHURCH_SLAVONIC_CYRILLIC = 68; // 0x44
     field public static final int OLD_HUNGARIAN = 76; // 0x4c
@@ -19283,6 +19306,7 @@
     field public static final int UNWRITTEN_LANGUAGES = 102; // 0x66
     field public static final int VAI = 99; // 0x63
     field public static final int VISIBLE_SPEECH = 100; // 0x64
+    field public static final int WANCHO = 188; // 0xbc
     field public static final int WARANG_CITI = 146; // 0x92
     field public static final int WESTERN_SYRIAC = 96; // 0x60
     field public static final int WOLEAI = 155; // 0x9b
@@ -20071,6 +20095,7 @@
     method public String getDateTimeFormat();
     method public String getDecimal();
     method public static android.icu.text.DateTimePatternGenerator getEmptyInstance();
+    method public String getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth);
     method public static android.icu.text.DateTimePatternGenerator getInstance();
     method public static android.icu.text.DateTimePatternGenerator getInstance(android.icu.util.ULocale);
     method public static android.icu.text.DateTimePatternGenerator getInstance(java.util.Locale);
@@ -20104,6 +20129,12 @@
     field public static final int ZONE = 15; // 0xf
   }
 
+  public enum DateTimePatternGenerator.DisplayWidth {
+    enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth ABBREVIATED;
+    enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth NARROW;
+    enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth WIDE;
+  }
+
   public static final class DateTimePatternGenerator.PatternInfo {
     ctor public DateTimePatternGenerator.PatternInfo();
     field public static final int BASE_CONFLICT = 1; // 0x1
@@ -21658,6 +21689,7 @@
     method public static boolean isAvailable(String, java.util.Date, java.util.Date);
     method public java.util.Currency toJavaCurrency();
     field public static final int LONG_NAME = 1; // 0x1
+    field public static final int NARROW_SYMBOL_NAME = 3; // 0x3
     field public static final int PLURAL_LONG_NAME = 2; // 0x2
     field public static final int SYMBOL_NAME = 0; // 0x0
   }
@@ -21860,6 +21892,7 @@
     ctor public JapaneseCalendar(int, int, int, int, int, int);
     field public static final int HEISEI;
     field public static final int MEIJI;
+    field public static final int REIWA;
     field public static final int SHOWA;
     field public static final int TAISHO;
   }
@@ -22335,6 +22368,8 @@
     field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
     field public static final android.icu.util.VersionInfo UNICODE_10_0;
     field public static final android.icu.util.VersionInfo UNICODE_11_0;
+    field public static final android.icu.util.VersionInfo UNICODE_12_0;
+    field public static final android.icu.util.VersionInfo UNICODE_12_1;
     field public static final android.icu.util.VersionInfo UNICODE_1_0;
     field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
     field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 469c964..9f4f314 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -4,4 +4,5 @@
     group graphics audio
     disabled
     oneshot
+    ioprio rt 0
     writepid /dev/stune/top-app/tasks
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 3bb9929..d4d5871 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -18,6 +18,7 @@
     tidy_checks: [
         "modernize-*",
         "-modernize-avoid-c-arrays",
+        "-modernize-use-trailing-return-type",
         "android-*",
         "misc-*",
         "readability-*",
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index f4a306e..f55acee 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <dirent.h>
+#include <fcntl.h>
 
 #include <set>
 #include <string>
@@ -69,7 +70,7 @@
 
 TEST(FileUtilsTests, ReadFile) {
   int pipefd[2];
-  ASSERT_EQ(pipe(pipefd), 0);
+  ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0);
 
   ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
   close(pipefd[1]);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 35c4ceb..96522f7 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -57,6 +57,7 @@
 import "frameworks/base/core/proto/android/view/enums.proto";
 import "frameworks/base/core/proto/android/wifi/enums.proto";
 import "frameworks/base/core/proto/android/stats/textclassifier/textclassifier_enums.proto";
+import "frameworks/base/core/proto/android/stats/otaupdate/updateengine_enums.proto";
 
 /**
  * The master atom class. This message defines all of the available
@@ -330,6 +331,8 @@
             222  [(log_from_module) = "textclassifier"];
         ExclusionRectStateChanged exclusion_rect_state_changed = 223;
         BackGesture back_gesture_reported_reported = 224;
+        UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
+        UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
     }
 
     // Pulled events will start at field 10000.
@@ -6872,6 +6875,9 @@
 
     // Relative word (exclusive) index of the end of the smart selection.
     optional int32 relative_suggested_word_end_index = 10;
+
+    // Name of source package.
+    optional string package_name = 11;
 }
 
 /**
@@ -6909,6 +6915,9 @@
 
     // Time spent on generating links in ms.
     optional int64 latency_millis = 10;
+
+    // Name of source package.
+    optional string package_name = 11;
 }
 
 /**
@@ -6940,6 +6949,9 @@
 
     // The score of the first entity type.
     optional float score = 8;
+
+    // Name of source package.
+    optional string package_name = 9;
 }
 
 /**
@@ -6968,4 +6980,74 @@
 
     // Position of this action.
     optional int32 action_index = 7;
+
+    // Name of source package.
+    optional string package_name = 8;
+}
+
+/**
+ * Information about an OTA update attempt by update_engine.
+ * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ */
+message UpdateEngineUpdateAttemptReported {
+    // The number of attempts for the update engine to apply a given payload.
+    optional int32 attempt_number = 1;
+
+    optional android.stats.otaupdate.PayloadType payload_type = 2;
+
+    // The total time in minutes for the update engine to apply a given payload.
+    // The time is calculated by calling clock_gettime() / CLOCK_BOOTTIME; and
+    // it's increased when the system is sleeping.
+    optional int32 duration_boottime_in_minutes = 3;
+
+    // The total time in minutes for the update engine to apply a given payload.
+    // The time is calculated by calling clock_gettime() / CLOCK_MONOTONIC_RAW;
+    // and it's not increased when the system is sleeping.
+    optional int32 duration_monotonic_in_minutes = 4;
+
+    // The size of the payload in MiBs.
+    optional int32 payload_size_mib = 5;
+
+    // The attempt result reported by the update engine for an OTA update.
+    optional android.stats.otaupdate.AttemptResult attempt_result = 6;
+
+    // The error code reported by the update engine after an OTA update attempt
+    // on A/B devices.
+    optional android.stats.otaupdate.ErrorCode error_code = 7;
+
+    // The build fingerprint of the source system. The value is read from a
+    // system property when the device takes the update. e.g.
+    // Android/aosp_sailfish/sailfish:10/QP1A.190425.004/5507117:userdebug/test-keys
+    optional string source_fingerprint = 8;
+}
+
+/**
+ * Information about all the attempts the device make before finishing the
+ * successful update.
+ * Logged from platform/system/update_engine/metrics_reporter_android.cc
+ */
+message UpdateEngineSuccessfulUpdateReported {
+    // The number of attempts for the update engine to apply the payload for a
+    // successful update.
+    optional int32 attempt_count = 1;
+
+    optional android.stats.otaupdate.PayloadType payload_type = 2;
+
+    optional int32 payload_size_mib = 3;
+
+    // The total number of bytes downloaded by update_engine since the last
+    // successful update.
+    optional int32 total_bytes_downloaded_mib = 4;
+
+    // The ratio in percentage of the over-downloaded bytes compared to the
+    // total bytes needed to successfully install the update. e.g. 200 if we
+    // download 200MiB in total for a 100MiB package.
+    optional int32 download_overhead_percentage = 5;
+
+    // The total time in minutes for the update engine to apply the payload for a
+    // successful update.
+    optional int32 total_duration_minutes = 6;
+
+    // The number of reboot of the device during a successful update.
+    optional int32 reboot_count = 7;
 }
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index eb53b7c..9f48f8a 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1174,214 +1174,11 @@
 Lcom/android/internal/statusbar/IStatusBarService$Stub;-><init>()V
 Lcom/android/internal/statusbar/IStatusBarService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/statusbar/IStatusBarService;
 Lcom/android/internal/telecom/ITelecomService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telecom/ITelecomService;
-Lcom/android/internal/telephony/Call$State;->ALERTING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->DIALING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->DISCONNECTED:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->DISCONNECTING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->HOLDING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->IDLE:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->INCOMING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->values()[Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->WAITING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call;-><init>()V
-Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery;Landroid/content/Context;)V
-Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;-><init>()V
-Lcom/android/internal/telephony/CallerInfoAsyncQuery;->release()V
-Lcom/android/internal/telephony/CallForwardInfo;-><init>()V
-Lcom/android/internal/telephony/CallTracker;-><init>()V
-Lcom/android/internal/telephony/cat/AppInterface$CommandType;->values()[Lcom/android/internal/telephony/cat/AppInterface$CommandType;
-Lcom/android/internal/telephony/cat/ResponseData;-><init>()V
-Lcom/android/internal/telephony/cat/ResultCode;->values()[Lcom/android/internal/telephony/cat/ResultCode;
-Lcom/android/internal/telephony/cat/RilMessageDecoder;->mCurrentRilMessage:Lcom/android/internal/telephony/cat/RilMessage;
-Lcom/android/internal/telephony/cat/RilMessageDecoder;->sendCmdForExecution(Lcom/android/internal/telephony/cat/RilMessage;)V
-Lcom/android/internal/telephony/cat/RilMessageDecoder;->sendStartDecodingMessageParams(Lcom/android/internal/telephony/cat/RilMessage;)V
-Lcom/android/internal/telephony/cat/ValueObject;-><init>()V
-Lcom/android/internal/telephony/cat/ValueParser;->retrieveDeviceIdentities(Lcom/android/internal/telephony/cat/ComprehensionTlv;)Lcom/android/internal/telephony/cat/DeviceIdentities;
-Lcom/android/internal/telephony/cdma/sms/BearerData$CodingException;-><init>(Ljava/lang/String;)V
-Lcom/android/internal/telephony/cdma/sms/BearerData$TimeStamp;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/BearerData;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/BearerData;->countAsciiSeptets(Ljava/lang/CharSequence;Z)I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->decodeUserDataPayload(Lcom/android/internal/telephony/cdma/sms/UserData;Z)V
-Lcom/android/internal/telephony/cdma/sms/BearerData;->displayMode:I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->encode(Lcom/android/internal/telephony/cdma/sms/BearerData;)[B
-Lcom/android/internal/telephony/cdma/sms/BearerData;->encode7bitAscii(Ljava/lang/String;Z)[B
-Lcom/android/internal/telephony/cdma/sms/BearerData;->getBitsForNumFields(II)I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->hasUserDataHeader:Z
-Lcom/android/internal/telephony/cdma/sms/BearerData;->messageId:I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->msgCenterTimeStamp:Lcom/android/internal/telephony/cdma/sms/BearerData$TimeStamp;
-Lcom/android/internal/telephony/cdma/sms/BearerData;->priority:I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->priorityIndicatorSet:Z
-Lcom/android/internal/telephony/cdma/sms/BearerData;->userData:Lcom/android/internal/telephony/cdma/sms/UserData;
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->digitMode:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberMode:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberOfDigits:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberPlan:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->parse(Ljava/lang/String;)Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->bearerData:[B
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->serviceCategory:I
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->teleService:I
-Lcom/android/internal/telephony/cdma/sms/UserData;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/UserData;->charToAscii:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/cdma/sms/UserData;->msgEncoding:I
-Lcom/android/internal/telephony/cdma/sms/UserData;->msgEncodingSet:Z
-Lcom/android/internal/telephony/cdma/sms/UserData;->numFields:I
-Lcom/android/internal/telephony/cdma/sms/UserData;->payload:[B
-Lcom/android/internal/telephony/cdma/sms/UserData;->payloadStr:Ljava/lang/String;
-Lcom/android/internal/telephony/cdma/sms/UserData;->userDataHeader:Lcom/android/internal/telephony/SmsHeader;
-Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;-><init>()V
-Lcom/android/internal/telephony/cdma/SmsMessage;-><init>()V
-Lcom/android/internal/telephony/cdma/SmsMessage;->calculateLength(Ljava/lang/CharSequence;ZZ)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
-Lcom/android/internal/telephony/cdma/SmsMessage;->createFromEfRecord(I[B)Lcom/android/internal/telephony/cdma/SmsMessage;
-Lcom/android/internal/telephony/cdma/SmsMessage;->createFromPdu([B)Lcom/android/internal/telephony/cdma/SmsMessage;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getIncomingSmsFingerprint()[B
-Lcom/android/internal/telephony/cdma/SmsMessage;->getMessageType()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->getNextMessageId()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->getNumOfVoicemails()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Lcom/android/internal/telephony/cdma/sms/UserData;Z)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Lcom/android/internal/telephony/cdma/sms/UserData;ZI)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;I[BZ)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/android/internal/telephony/SmsHeader;)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/android/internal/telephony/SmsHeader;I)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getTeleService()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->isStatusReportMessage()Z
-Lcom/android/internal/telephony/cdma/SmsMessage;->mBearerData:Lcom/android/internal/telephony/cdma/sms/BearerData;
-Lcom/android/internal/telephony/cdma/SmsMessage;->mEnvelope:Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;
-Lcom/android/internal/telephony/cdma/SmsMessage;->parseSms()V
-Lcom/android/internal/telephony/cdma/SmsMessage;->privateGetSubmitPdu(Ljava/lang/String;ZLcom/android/internal/telephony/cdma/sms/UserData;)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->DORMANT:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->values()[Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$State;->CONNECTED:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->CONNECTING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->DISCONNECTING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->FAILED:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->IDLE:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->RETRYING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->values()[Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DriverCall$State;->values()[Lcom/android/internal/telephony/DriverCall$State;
-Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler$SmsCbConcatInfo;-><init>(Lcom/android/internal/telephony/gsm/SmsCbHeader;Landroid/telephony/SmsCbLocation;)V
-Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler$SmsCbConcatInfo;->matchesLocation(Ljava/lang/String;II)Z
-Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler;->mSmsCbPageMap:Ljava/util/HashMap;
-Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;->acknowledgeLastIncomingSms(ZILandroid/os/Message;)V
-Lcom/android/internal/telephony/gsm/GsmMmiCode;-><init>(Lcom/android/internal/telephony/GsmCdmaPhone;Lcom/android/internal/telephony/uicc/UiccCardApplication;)V
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->getCLIRMode()I
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->getScString()Ljava/lang/CharSequence;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isActivate()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isDeactivate()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isErasure()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isInterrogate()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isRegister()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isServiceCodeCallBarring(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isServiceCodeCallForwarding(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isTemporaryModeCLIR()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->makeEmptyNull(Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mContext:Landroid/content/Context;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mDialingNumber:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mIccRecords:Lcom/android/internal/telephony/uicc/IccRecords;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mPhone:Lcom/android/internal/telephony/GsmCdmaPhone;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSc:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSia:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSib:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSic:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->newFromDialString(Ljava/lang/String;Lcom/android/internal/telephony/GsmCdmaPhone;Lcom/android/internal/telephony/uicc/UiccCardApplication;)Lcom/android/internal/telephony/gsm/GsmMmiCode;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->processCode()V
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->siToServiceClass(Ljava/lang/String;)I
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->sPatternSuppService:Ljava/util/regex/Pattern;
-Lcom/android/internal/telephony/gsm/GsmSmsAddress;-><init>([BII)V
-Lcom/android/internal/telephony/gsm/GsmSmsAddress;->isCphsVoiceMessageClear()Z
-Lcom/android/internal/telephony/gsm/GsmSmsAddress;->isCphsVoiceMessageSet()Z
-Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->getFormat()Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->mGsmInboundSmsHandler:Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;
-Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->sendSms(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/gsm/SimTlv;-><init>([BII)V
-Lcom/android/internal/telephony/gsm/SimTlv;->getData()[B
-Lcom/android/internal/telephony/gsm/SimTlv;->getTag()I
-Lcom/android/internal/telephony/gsm/SimTlv;->isValidObject()Z
-Lcom/android/internal/telephony/gsm/SimTlv;->mHasValidTlvObject:Z
-Lcom/android/internal/telephony/gsm/SimTlv;->nextObject()Z
-Lcom/android/internal/telephony/gsm/SmsCbHeader;-><init>([B)V
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getGeographicalScope()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getNumberOfPages()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getPageIndex()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getSerialNumber()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getServiceCategory()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->mMessageIdentifier:I
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;-><init>([B)V
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getByte()I
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getUserData()[B
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getUserDataUCS2(I)Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mCur:I
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mPdu:[B
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mUserDataSeptetPadding:I
-Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;-><init>()V
-Lcom/android/internal/telephony/gsm/SmsMessage;-><init>()V
-Lcom/android/internal/telephony/gsm/SmsMessage;->calculateLength(Ljava/lang/CharSequence;Z)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
-Lcom/android/internal/telephony/gsm/SmsMessage;->createFromEfRecord(I[B)Lcom/android/internal/telephony/gsm/SmsMessage;
-Lcom/android/internal/telephony/gsm/SmsMessage;->createFromPdu([B)Lcom/android/internal/telephony/gsm/SmsMessage;
-Lcom/android/internal/telephony/gsm/SmsMessage;->encodeUCS2(Ljava/lang/String;[B)[B
-Lcom/android/internal/telephony/gsm/SmsMessage;->getStatus()I
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZI)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[B)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[BIII)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[BIIII)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPduHead(Ljava/lang/String;Ljava/lang/String;BZLcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;)Ljava/io/ByteArrayOutputStream;
-Lcom/android/internal/telephony/gsm/SmsMessage;->isMWIClearMessage()Z
-Lcom/android/internal/telephony/gsm/SmsMessage;->isMwiDontStore()Z
-Lcom/android/internal/telephony/gsm/SmsMessage;->isMWISetMessage()Z
-Lcom/android/internal/telephony/gsm/SmsMessage;->isStatusReportMessage()Z
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->loadEfFilesFromUsim()Ljava/util/ArrayList;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mFh:Lcom/android/internal/telephony/uicc/IccFileHandler;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mLock:Ljava/lang/Object;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mPhoneBookRecords:Ljava/util/ArrayList;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->reset()V
-Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
-Lcom/android/internal/telephony/GsmCdmaConnection$MyHandler;-><init>(Lcom/android/internal/telephony/GsmCdmaConnection;Landroid/os/Looper;)V
-Lcom/android/internal/telephony/IccCardConstants$State;->ABSENT:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->CARD_IO_ERROR:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->NETWORK_LOCKED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->NOT_READY:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->PERM_DISABLED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->PIN_REQUIRED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->PUK_REQUIRED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->READY:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->UNKNOWN:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->values()[Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccProvider;-><init>()V
 Lcom/android/internal/telephony/IIccPhoneBook$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Lcom/android/internal/telephony/IIccPhoneBook$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IIccPhoneBook;
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsInEf(I)Ljava/util/List;
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsInEfForSubscriber(II)Ljava/util/List;
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsSize(I)[I
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsSizeForSubscriber(II)[I
-Lcom/android/internal/telephony/IIccPhoneBook;->updateAdnRecordsInEfBySearch(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
 Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker$ExternalCallStateListener;-><init>(Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;)V
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker$ExternalConnectionListener;-><init>(Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;)V
-Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyCallForwardingIndicator()V
-Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyPreciseCallStateChanged()V
-Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->getImsCall()Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->findConnection(Lcom/android/ims/ImsCall;)Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->getEcbmInterface()Lcom/android/ims/ImsEcbm;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mCallExpectedToResume:Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mImsCallListener:Lcom/android/ims/ImsCall$Listener;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mImsManager:Lcom/android/ims/ImsManager;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mUssdSession:Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->processCallStateChange(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;I)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->processCallStateChange(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;IZ)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->setVideoCallProvider(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;Lcom/android/ims/ImsCall;)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneConnection$MyHandler;-><init>(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;Landroid/os/Looper;)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->mImsCall:Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->update(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;)Z
-Lcom/android/internal/telephony/InboundSmsHandler$SmsBroadcastReceiver;-><init>(Lcom/android/internal/telephony/InboundSmsHandler;Lcom/android/internal/telephony/InboundSmsTracker;)V
 Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
 Lcom/android/internal/telephony/ISms$Stub;-><init>()V
@@ -1389,8 +1186,6 @@
 Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ISub$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISub;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
 Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
@@ -1400,82 +1195,6 @@
 Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
 Lcom/android/internal/telephony/IWapPushManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IWapPushManager;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->CONNECTED:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->CONNECTING:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->DISCONNECTED:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->SUSPENDED:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->values()[Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$State;->IDLE:Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants$State;->OFFHOOK:Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants$State;->RINGING:Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants$State;->values()[Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_ALLOWED:I
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_PAYPHONE:I
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_RESTRICTED:I
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_UNKNOWN:I
-Lcom/android/internal/telephony/RILConstants;->PREFERRED_NETWORK_MODE:I
-Lcom/android/internal/telephony/sip/SipPhone$SipCall;->hold()V
-Lcom/android/internal/telephony/sip/SipPhone$SipCall;->switchWith(Lcom/android/internal/telephony/sip/SipPhone$SipCall;)V
-Lcom/android/internal/telephony/sip/SipPhone$SipCall;->unhold()V
-Lcom/android/internal/telephony/sip/SipPhone;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/sip/SipPhone;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/sip/SipPhone;->mBackgroundCall:Lcom/android/internal/telephony/sip/SipPhone$SipCall;
-Lcom/android/internal/telephony/sip/SipPhone;->mForegroundCall:Lcom/android/internal/telephony/sip/SipPhone$SipCall;
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->DBG:Z
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableCDMA:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableCommon:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableGSM:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/SmsApplication$SmsApplicationData;->mApplicationName:Ljava/lang/String;
-Lcom/android/internal/telephony/SmsApplication;->configurePreferredActivity(Landroid/content/pm/PackageManager;Landroid/content/ComponentName;I)V
-Lcom/android/internal/telephony/SmsApplication;->getApplicationCollection(Landroid/content/Context;)Ljava/util/Collection;
-Lcom/android/internal/telephony/SmsApplication;->getDefaultMmsApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
-Lcom/android/internal/telephony/SmsApplication;->getDefaultRespondViaMessageApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
-Lcom/android/internal/telephony/SmsApplication;->getDefaultSmsApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
-Lcom/android/internal/telephony/SmsApplication;->getSmsApplicationData(Ljava/lang/String;Landroid/content/Context;)Lcom/android/internal/telephony/SmsApplication$SmsApplicationData;
-Lcom/android/internal/telephony/SmsApplication;->isDefaultSmsApplication(Landroid/content/Context;Ljava/lang/String;)Z
-Lcom/android/internal/telephony/SmsApplication;->setDefaultApplication(Ljava/lang/String;Landroid/content/Context;)V
-Lcom/android/internal/telephony/SmsApplication;->shouldWriteMessageForPackage(Ljava/lang/String;Landroid/content/Context;)Z
-Lcom/android/internal/telephony/SMSDispatcher$DataSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Ljava/util/ArrayList;[Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSenderCallback;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;)V
-Lcom/android/internal/telephony/SMSDispatcher$SmsSenderCallback;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsSender;)V
-Lcom/android/internal/telephony/SMSDispatcher$TextSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
-Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
-Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
-Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String;
-Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->acceptCall(Landroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->mDcSuccess:Z
-Lcom/android/internal/telephony/test/SimulatedCommands;->resultFail(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->resultSuccess(Landroid/os/Message;Ljava/lang/Object;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->simulatedCallState:Lcom/android/internal/telephony/test/SimulatedGsmCallState;
-Lcom/android/internal/telephony/test/SimulatedCommands;->unimplemented(Landroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;->getInstance()Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;
-Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;->setCallForward(IIILjava/lang/String;ILandroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->conference()Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->onChld(CC)Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->releaseActiveAcceptHeldOrWaiting()Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->releaseHeldOrUDUB()Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->separateCall(I)Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->switchActiveAndHeldOrWaiting()Z
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;-><init>()V
-Lcom/android/internal/telephony/uicc/IccRefreshResponse;-><init>()V
-Lcom/android/internal/telephony/uicc/IccUtils;->adnStringFieldToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->bcdToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->cdmaBcdByteToInt(B)I
-Lcom/android/internal/telephony/uicc/IccUtils;->cdmaBcdToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->gsmBcdByteToInt(B)I
-Lcom/android/internal/telephony/uicc/IccUtils;->hexCharToInt(C)I
-Lcom/android/internal/telephony/uicc/IccUtils;->hexStringToBytes(Ljava/lang/String;)[B
-Lcom/android/internal/telephony/uicc/IccUtils;->networkNameToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->parseToBnW([BI)Landroid/graphics/Bitmap;
-Lcom/android/internal/telephony/uicc/IccUtils;->parseToRGB([BIZ)Landroid/graphics/Bitmap;
-Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->values()[Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
 Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/util/MemInfoReader;-><init>()V
 Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -1488,283 +1207,5 @@
 Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
 Lcom/google/android/gles_jni/EGLImpl;-><init>()V
 Lcom/google/android/gles_jni/GLImpl;-><init>()V
-Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>()V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
-Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
-Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
-Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
-Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
-Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
-Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
-Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
-Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
-Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
-Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
-Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
-Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
-Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
-Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/PduBody;-><init>()V
-Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
-Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
-Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
-Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
-Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
-Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
-Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
-Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
-Lcom/google/android/mms/pdu/PduComposer;->make()[B
-Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
-Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
-Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
-Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
-Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
-Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
-Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
-Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
-Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
 Lcom/google/android/mms/pdu/PduParser;->$assertionsDisabled:Z
-Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
-Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
-Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
-Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
-Lcom/google/android/mms/pdu/PduPart;-><init>()V
-Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPart;->getCharset()I
-Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
-Lcom/google/android/mms/pdu/PduPart;->getData()[B
-Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
-Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
-Lcom/google/android/mms/pdu/PduPart;->getName()[B
-Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
-Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
-Lcom/google/android/mms/pdu/PduPart;->setData([B)V
-Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
-Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
-Lcom/google/android/mms/pdu/PduPart;->setName([B)V
-Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
-Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
-Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
-Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
-Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
-Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
-Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
-Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
-Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
-Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
-Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
-Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/SendConf;-><init>()V
-Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
-Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;-><init>()V
-Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
-Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
-Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
-Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
-Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
-Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
-Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
-Lcom/google/android/mms/util/AbstractCache;-><init>()V
-Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
-Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
-Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
-Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
-Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
-Lcom/google/android/mms/util/PduCache;-><init>()V
-Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
-Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
-Lcom/google/android/mms/util/PduCache;->purgeAll()V
-Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
-Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
-Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
-Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
-Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
-Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
-Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
-Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 827e540..4730bd0 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -391,7 +391,8 @@
         void onPerformGestureResult(int sequence, boolean completedSuccessfully);
         void onFingerprintCapturingGesturesChanged(boolean active);
         void onFingerprintGesture(int gesture);
-        void onAccessibilityButtonClicked();
+        /** Accessbility button clicked callbacks for different displays */
+        void onAccessibilityButtonClicked(int displayId);
         void onAccessibilityButtonAvailabilityChanged(boolean available);
     }
 
@@ -459,7 +460,8 @@
     private final SparseArray<MagnificationController> mMagnificationControllers =
             new SparseArray<>(0);
     private SoftKeyboardController mSoftKeyboardController;
-    private AccessibilityButtonController mAccessibilityButtonController;
+    private final SparseArray<AccessibilityButtonController> mAccessibilityButtonControllers =
+            new SparseArray<>(0);
 
     private int mGestureStatusCallbackSequence;
 
@@ -1521,17 +1523,40 @@
      */
     @NonNull
     public final AccessibilityButtonController getAccessibilityButtonController() {
+        return getAccessibilityButtonController(Display.DEFAULT_DISPLAY);
+    }
+
+    /**
+     * Returns the controller of specified logical display for the accessibility button within the
+     * system's navigation area. This instance may be used to query the accessibility button's
+     * state and register listeners for interactions with and state changes for the accessibility
+     * button when {@link AccessibilityServiceInfo#FLAG_REQUEST_ACCESSIBILITY_BUTTON} is set.
+     * <p>
+     * <strong>Note:</strong> Not all devices are capable of displaying the accessibility button
+     * within a navigation area, and as such, use of this class should be considered only as an
+     * optional feature or shortcut on supported device implementations.
+     * </p>
+     *
+     * @param displayId The logic display id, use {@link Display#DEFAULT_DISPLAY} for default
+     *                  display.
+     * @return the accessibility button controller for this {@link AccessibilityService}
+     */
+    @NonNull
+    public final AccessibilityButtonController getAccessibilityButtonController(int displayId) {
         synchronized (mLock) {
-            if (mAccessibilityButtonController == null) {
-                mAccessibilityButtonController = new AccessibilityButtonController(
+            AccessibilityButtonController controller = mAccessibilityButtonControllers.get(
+                    displayId);
+            if (controller == null) {
+                controller = new AccessibilityButtonController(
                         AccessibilityInteractionClient.getInstance().getConnection(mConnectionId));
+                mAccessibilityButtonControllers.put(displayId, controller);
             }
-            return mAccessibilityButtonController;
+            return controller;
         }
     }
 
-    private void onAccessibilityButtonClicked() {
-        getAccessibilityButtonController().dispatchAccessibilityButtonClicked();
+    private void onAccessibilityButtonClicked(int displayId) {
+        getAccessibilityButtonController(displayId).dispatchAccessibilityButtonClicked();
     }
 
     private void onAccessibilityButtonAvailabilityChanged(boolean available) {
@@ -1737,8 +1762,8 @@
             }
 
             @Override
-            public void onAccessibilityButtonClicked() {
-                AccessibilityService.this.onAccessibilityButtonClicked();
+            public void onAccessibilityButtonClicked(int displayId) {
+                AccessibilityService.this.onAccessibilityButtonClicked(displayId);
             }
 
             @Override
@@ -1852,8 +1877,10 @@
             mCaller.sendMessage(mCaller.obtainMessageI(DO_ON_FINGERPRINT_GESTURE, gesture));
         }
 
-        public void onAccessibilityButtonClicked() {
-            final Message message = mCaller.obtainMessage(DO_ACCESSIBILITY_BUTTON_CLICKED);
+        /** Accessibility button clicked callbacks for different displays */
+        public void onAccessibilityButtonClicked(int displayId) {
+            final Message message = mCaller.obtainMessageI(DO_ACCESSIBILITY_BUTTON_CLICKED,
+                    displayId);
             mCaller.sendMessage(message);
         }
 
@@ -1987,7 +2014,7 @@
 
                 case (DO_ACCESSIBILITY_BUTTON_CLICKED): {
                     if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
-                        mCallback.onAccessibilityButtonClicked();
+                        mCallback.onAccessibilityButtonClicked(message.arg1);
                     }
                 } return;
 
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 407ba59..e0d5e44 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -52,7 +52,7 @@
 
     void onFingerprintGesture(int gesture);
 
-    void onAccessibilityButtonClicked();
+    void onAccessibilityButtonClicked(int displayId);
 
     void onAccessibilityButtonAvailabilityChanged(boolean available);
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 553ef69..a6784780 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -191,6 +191,8 @@
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Method;
 import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -6435,6 +6437,26 @@
         NetworkSecurityConfigProvider.install(appContext);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
+
+        if (isAppDebuggable) {
+            try {
+                // Load all the agents in the code_cache/startup_agents directory.
+                // We pass the absolute path to the data_dir as an argument.
+                Path startup_path = appContext.getCodeCacheDir().toPath().resolve("startup_agents");
+                if (Files.exists(startup_path)) {
+                    for (Path p : Files.newDirectoryStream(startup_path)) {
+                        handleAttachAgent(
+                                p.toAbsolutePath().toString()
+                                + "="
+                                + appContext.getDataDir().toPath().toAbsolutePath().toString(),
+                                data.info);
+                    }
+                }
+            } catch (Exception e) {
+                // Ignored.
+            }
+        }
+
         // Continue loading instrumentation.
         if (ii != null) {
             ApplicationInfo instrApp;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 41733b3..9720e9f 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -50,6 +50,7 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
+import android.view.SurfaceControl;
 import android.view.ViewConfiguration;
 import android.view.Window;
 import android.view.WindowManagerGlobal;
@@ -528,6 +529,12 @@
             } while (mWaitingActivities.contains(aw));
 
             waitForEnterAnimationComplete(aw.activity);
+
+            // Apply an empty transaction to ensure SF has a chance to update before
+            // the Activity is ready (b/138263890).
+            try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+                t.apply(true);
+            }
             return aw.activity;
         }
     }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index c58972e..635b9b0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -112,7 +112,6 @@
 import android.net.lowpan.LowpanManager;
 import android.net.nsd.INsdManager;
 import android.net.nsd.NsdManager;
-import android.net.wifi.IWifiManager;
 import android.net.wifi.IWifiScanner;
 import android.net.wifi.RttManager;
 import android.net.wifi.WifiManager;
@@ -730,10 +729,8 @@
         registerService(Context.WIFI_SERVICE, WifiManager.class,
                 new CachedServiceFetcher<WifiManager>() {
             @Override
-            public WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
-                IWifiManager service = IWifiManager.Stub.asInterface(b);
-                return new WifiManager(ctx.getOuterContext(), service,
+            public WifiManager createService(ContextImpl ctx) {
+                return new WifiManager(ctx.getOuterContext(),
                         ConnectivityThread.getInstanceLooper());
             }});
 
@@ -1166,7 +1163,8 @@
             @Override
             public AppPredictionManager createService(ContextImpl ctx)
                     throws ServiceNotFoundException {
-                return new AppPredictionManager(ctx);
+                IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
+                return b == null ? null : new AppPredictionManager(ctx);
             }
         });
 
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 2b74b99..ed2b991 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1289,7 +1289,7 @@
                 }
 
                 @Override
-                public void onAccessibilityButtonClicked() {
+                public void onAccessibilityButtonClicked(int displayId) {
                     /* do nothing */
                 }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 49cfd41..ff5a043 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4991,26 +4991,60 @@
      * call {@link android.security.KeyChain#choosePrivateKeyAlias} first.
      *
      * The grantee app will receive the {@link android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED}
-     * broadcast when access to a key is granted or revoked.
+     * broadcast when access to a key is granted.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
      *        {@code null} if calling from a delegated certificate installer.
      * @param alias The alias of the key to grant access to.
      * @param packageName The name of the (already installed) package to grant access to.
-     * @param hasGrant Whether to grant access to the alias or revoke it.
      * @return {@code true} if the grant was set successfully, {@code false} otherwise.
      *
-     * @throws SecurityException if the caller is not a device owner, a profile  owner or
+     * @throws SecurityException if the caller is not a device owner, a profile owner or
      *         delegated certificate chooser.
      * @throws IllegalArgumentException if {@code packageName} or {@code alias} are empty, or if
      *         {@code packageName} is not a name of an installed package.
+     * @see #revokeKeyPairFromApp
      */
-    public boolean setKeyGrantForApp(@Nullable ComponentName admin, @NonNull String alias,
-            @NonNull String packageName, boolean hasGrant) {
-        throwIfParentInstance("addKeyGrant");
+    public boolean grantKeyPairToApp(@Nullable ComponentName admin, @NonNull String alias,
+            @NonNull String packageName) {
+        throwIfParentInstance("grantKeyPairToApp");
         try {
             return mService.setKeyGrantForApp(
-                    admin, mContext.getPackageName(), alias, packageName, hasGrant);
+                    admin, mContext.getPackageName(), alias, packageName, true);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
+     * Called by a device or profile owner, or delegated certificate chooser (an app that has been
+     * delegated the {@link #DELEGATION_CERT_SELECTION} privilege), to revoke an application's
+     * grant to a KeyChain key pair.
+     * Calls by the application to {@link android.security.KeyChain#getPrivateKey}
+     * will fail after the grant is revoked.
+     *
+     * The grantee app will receive the {@link android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED}
+     * broadcast when access to a key is revoked.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+     *        {@code null} if calling from a delegated certificate installer.
+     * @param alias The alias of the key to revoke access from.
+     * @param packageName The name of the (already installed) package to revoke access from.
+     * @return {@code true} if the grant was revoked successfully, {@code false} otherwise.
+     *
+     * @throws SecurityException if the caller is not a device owner, a profile owner or
+     *         delegated certificate chooser.
+     * @throws IllegalArgumentException if {@code packageName} or {@code alias} are empty, or if
+     *         {@code packageName} is not a name of an installed package.
+     * @see #grantKeyPairToApp
+     */
+    public boolean revokeKeyPairFromApp(@Nullable ComponentName admin, @NonNull String alias,
+            @NonNull String packageName) {
+        throwIfParentInstance("revokeKeyPairFromApp");
+        try {
+            return mService.setKeyGrantForApp(
+                    admin, mContext.getPackageName(), alias, packageName, false);
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 0ccd49f..5e530ee 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -355,7 +355,8 @@
     @Override
     public Bundle call(String method, String arg, Bundle extras) {
         if (method.equals(METHOD_SLICE)) {
-            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+            Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
+                    extras.getParcelable(EXTRA_BIND_URI)));
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
 
             String callingPackage = getCallingPackage();
@@ -369,7 +370,7 @@
         } else if (method.equals(METHOD_MAP_INTENT)) {
             Intent intent = extras.getParcelable(EXTRA_INTENT);
             if (intent == null) return null;
-            Uri uri = onMapIntentToUri(intent);
+            Uri uri = validateIncomingUriOrNull(onMapIntentToUri(intent));
             List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
             Bundle b = new Bundle();
             if (uri != null) {
@@ -383,24 +384,27 @@
         } else if (method.equals(METHOD_MAP_ONLY_INTENT)) {
             Intent intent = extras.getParcelable(EXTRA_INTENT);
             if (intent == null) return null;
-            Uri uri = onMapIntentToUri(intent);
+            Uri uri = validateIncomingUriOrNull(onMapIntentToUri(intent));
             Bundle b = new Bundle();
             b.putParcelable(EXTRA_SLICE, uri);
             return b;
         } else if (method.equals(METHOD_PIN)) {
-            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+            Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
+                    extras.getParcelable(EXTRA_BIND_URI)));
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Only the system can pin/unpin slices");
             }
             handlePinSlice(uri);
         } else if (method.equals(METHOD_UNPIN)) {
-            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+            Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
+                    extras.getParcelable(EXTRA_BIND_URI)));
             if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                 throw new SecurityException("Only the system can pin/unpin slices");
             }
             handleUnpinSlice(uri);
         } else if (method.equals(METHOD_GET_DESCENDANTS)) {
-            Uri uri = getUriWithoutUserId(extras.getParcelable(EXTRA_BIND_URI));
+            Uri uri = getUriWithoutUserId(
+                    validateIncomingUriOrNull(extras.getParcelable(EXTRA_BIND_URI)));
             Bundle b = new Bundle();
             b.putParcelableArrayList(EXTRA_SLICE_DESCENDANTS,
                     new ArrayList<>(handleGetDescendants(uri)));
@@ -416,6 +420,10 @@
         return super.call(method, arg, extras);
     }
 
+    private Uri validateIncomingUriOrNull(Uri uri) {
+        return uri == null ? null : validateIncomingUri(uri);
+    }
+
     private Collection<Uri> handleGetDescendants(Uri uri) {
         mCallback = "onGetSliceDescendants";
         return onGetSliceDescendants(uri);
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index e4114c6..ac40150 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -127,6 +127,12 @@
         public Builder() {}
 
         /**
+         * Whether only a single device should match the provided filter.
+         *
+         * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac
+         * address, bonded devices are also searched among. This allows to obtain the necessary app
+         * privileges even if the device is already paired.
+         *
          * @param singleDevice if true, scanning for a device will stop as soon as at least one
          *                     fitting device is found
          */
diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/core/java/android/compat/IPlatformCompat.aidl
similarity index 90%
rename from services/core/java/com/android/server/compat/IPlatformCompat.aidl
rename to core/java/android/compat/IPlatformCompat.aidl
index 8ab08f9..3d8a9d5 100644
--- a/services/core/java/com/android/server/compat/IPlatformCompat.aidl
+++ b/core/java/android/compat/IPlatformCompat.aidl
@@ -14,12 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.server.compat;
+package android.compat;
 
 import android.content.pm.ApplicationInfo;
 
 /**
- * System private API for talking with the PlatformCompat service.
+ * Platform private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes.
+ * For app processes please use android.compat.Compatibility API.
+ *
  * {@hide}
  */
 interface IPlatformCompat
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4ea3726..65c11d7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.MODE_ERRORED;
@@ -645,9 +646,11 @@
     }
 
     boolean checkUser(int pid, int uid, Context context) {
-        return UserHandle.getUserId(uid) == context.getUserId()
-                || mSingleUser
-                || context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+        if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+            return true;
+        }
+        return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+                || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
                 == PERMISSION_GRANTED;
     }
 
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 73bc908..2c53faa 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -38,6 +38,7 @@
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.app.VrManager;
+import android.compat.IPlatformCompat;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
@@ -3228,6 +3229,7 @@
             ROLE_SERVICE,
             //@hide ROLE_CONTROLLER_SERVICE,
             CAMERA_SERVICE,
+            //@hide: PLATFORM_COMPAT_SERVICE,
             PRINT_SERVICE,
             CONSUMER_IR_SERVICE,
             //@hide: TRUST_SERVICE,
@@ -3735,6 +3737,14 @@
     public static final String NETWORK_STACK_SERVICE = "network_stack";
 
     /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link android.net.WifiStackClient} IBinder for communicating with the network stack
+     * @hide
+     * @see android.net.WifiStackClient
+     */
+    public static final String WIFI_STACK_SERVICE = "wifi_stack";
+
+    /**
      * Use with {@link #getSystemService(String)} to retrieve a
      * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
      * IPSec.
@@ -4109,6 +4119,9 @@
     /**
      * Official published name of the app prediction service.
      *
+     * <p><b>NOTE: </b> this service is optional; callers of
+     * {@code Context.getSystemServiceName(APP_PREDICTION_SERVICE)} should check for {@code null}.
+     *
      * @hide
      * @see #getSystemService(String)
      */
@@ -4586,6 +4599,13 @@
     public static final String STATS_MANAGER = "stats";
 
     /**
+     * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+     * {@link IPlatformCompat} IBinder for communicating with the platform compat service.
+     * @hide
+     */
+    public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
+
+    /**
      * Service to capture a bugreport.
      * @see #getSystemService(String)
      * @see android.os.BugreportManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 895eba6..c561013 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -38,6 +38,8 @@
 import android.app.PackageInstallObserver;
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.StorageStatsManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -3369,6 +3371,17 @@
      */
     public static final int VERSION_CODE_HIGHEST = -1;
 
+    /**
+     * Apps targeting Android R and above will need to declare the packages and intents they intend
+     * to use to get details about other apps on a device. Such declarations must be made via the
+     * {@code <queries>} tag in the manifest.
+     *
+     * @hide
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    public static final long FILTER_APPLICATION_QUERY = 135549675L;
+
     /** {@hide} */
     public int getUserId() {
         return UserHandle.myUserId();
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 2f198ac..f50502e 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -675,12 +675,6 @@
             "android.content.pm.extra.ENABLE_ROLLBACK_INSTALL_FLAGS";
 
     /**
-     * Extra field name for the set of installed users for a given rollback package.
-     */
-    public static final String EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS =
-            "android.content.pm.extra.ENABLE_ROLLBACK_INSTALLED_USERS";
-
-    /**
      * Extra field name for the user id an install is associated with when
      * enabling rollback.
      */
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 1c37c64..fcdc81c 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -16,11 +16,17 @@
 
 package android.content.pm;
 
+import android.annotation.IntDef;
 import android.annotation.UnsupportedAppUsage;
+import android.annotation.UserIdInt;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.DebugUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Per-user information.
@@ -119,10 +125,31 @@
      */
     public static final int FLAG_SYSTEM = 0x00000800;
 
+    /**
+     * @hide
+     */
+    @IntDef(flag = true, prefix = "FLAG_", value = {
+            FLAG_PRIMARY,
+            FLAG_ADMIN,
+            FLAG_GUEST,
+            FLAG_RESTRICTED,
+            FLAG_INITIALIZED,
+            FLAG_MANAGED_PROFILE,
+            FLAG_DISABLED,
+            FLAG_QUIET_MODE,
+            FLAG_EPHEMERAL,
+            FLAG_DEMO,
+            FLAG_FULL,
+            FLAG_SYSTEM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface UserInfoFlag {
+    }
+
     public static final int NO_PROFILE_GROUP_ID = UserHandle.USER_NULL;
 
     @UnsupportedAppUsage
-    public int id;
+    public @UserIdInt int id;
     @UnsupportedAppUsage
     public int serialNumber;
     @UnsupportedAppUsage
@@ -130,7 +157,7 @@
     @UnsupportedAppUsage
     public String iconPath;
     @UnsupportedAppUsage
-    public int flags;
+    public @UserInfoFlag int flags;
     @UnsupportedAppUsage
     public long creationTime;
     @UnsupportedAppUsage
@@ -214,6 +241,10 @@
         return (flags & FLAG_DEMO) == FLAG_DEMO;
     }
 
+    public boolean isFull() {
+        return (flags & FLAG_FULL) == FLAG_FULL;
+    }
+
     /**
      * Returns true if the user is a split system user.
      * <p>If {@link UserManager#isSplitSystemUser split system user mode} is not enabled,
@@ -290,13 +321,23 @@
 
     @Override
     public String toString() {
+        // NOTE:  do not change this string, it's used by 'pm list users', which in turn is
+        // used and parsed by TestDevice. In other words, if you change it, you'd have to change
+        // TestDevice, TestDeviceTest, and possibly others....
         return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
     }
 
+    /** @hide */
+    public static String flagsToString(int flags) {
+        return DebugUtils.flagsToString(UserInfo.class, "FLAG_", flags);
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
 
+    @Override
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         dest.writeInt(id);
         dest.writeString(name);
diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java
index 2014751..c89796d 100644
--- a/core/java/android/content/rollback/PackageRollbackInfo.java
+++ b/core/java/android/content/rollback/PackageRollbackInfo.java
@@ -76,10 +76,10 @@
     private final boolean mIsApex;
 
     /*
-     * The list of users the package is installed for.
+     * The list of users for which snapshots have been saved.
      */
     // NOTE: Not a part of the Parcelable representation of this object.
-    private final IntArray mInstalledUsers;
+    private final IntArray mSnapshottedUsers;
 
     /**
      * A mapping between user and an inode of theirs CE data snapshot.
@@ -148,8 +148,8 @@
     }
 
     /** @hide */
-    public IntArray getInstalledUsers() {
-        return mInstalledUsers;
+    public IntArray getSnapshottedUsers() {
+        return mSnapshottedUsers;
     }
 
     /** @hide */
@@ -179,14 +179,14 @@
     public PackageRollbackInfo(VersionedPackage packageRolledBackFrom,
             VersionedPackage packageRolledBackTo,
             @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores,
-            boolean isApex, @NonNull IntArray installedUsers,
+            boolean isApex, @NonNull IntArray snapshottedUsers,
             @NonNull SparseLongArray ceSnapshotInodes) {
         this.mVersionRolledBackFrom = packageRolledBackFrom;
         this.mVersionRolledBackTo = packageRolledBackTo;
         this.mPendingBackups = pendingBackups;
         this.mPendingRestores = pendingRestores;
         this.mIsApex = isApex;
-        this.mInstalledUsers = installedUsers;
+        this.mSnapshottedUsers = snapshottedUsers;
         this.mCeSnapshotInodes = ceSnapshotInodes;
     }
 
@@ -196,7 +196,7 @@
         this.mIsApex = in.readBoolean();
         this.mPendingRestores = null;
         this.mPendingBackups = null;
-        this.mInstalledUsers = null;
+        this.mSnapshottedUsers = null;
         this.mCeSnapshotInodes = null;
     }
 
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 73b8a48..1609f53 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -74,7 +74,10 @@
     }
 
     /**
-     * Returns a list of all currently available rollbacks.
+     * Returns a list of all currently available rollbacks. This includes ones for very recently
+     * installed packages (even if onFinished has not yet been called). As a result, packages that
+     * very recently failed to install may also be included, but those rollbacks will fail with
+     * 'rollback not available'.
      *
      * @throws SecurityException if the caller does not have appropriate permissions.
      */
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 099ae29..e78fb7f 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -339,6 +339,8 @@
      * for {@link #TYPE_STEP_COUNTER} instead. It is defined as a
      * {@link Sensor#REPORTING_MODE_SPECIAL_TRIGGER} sensor.
      * <p>
+     * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
+     * <p>
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
      */
     public static final int TYPE_STEP_DETECTOR = 18;
@@ -384,8 +386,6 @@
      * gyroscope. This sensor uses lower power than the other rotation vectors, because it doesn't
      * use the gyroscope. However, it is more noisy and will work best outdoors.
      * <p>
-     * This sensor requires permission {@code android.permission.ACTIVITY_RECOGNITION}.
-     * <p>
      * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
      */
     public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20;
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1142a07..fb6b231 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -100,9 +100,12 @@
     /**
      * @hide
      */
-    public static final int DISMISSED_REASON_POSITIVE = 1;
+    public static final int DISMISSED_REASON_CONFIRMED = 1;
 
     /**
+     * Dialog is done animating away after user clicked on the button set via
+     * {@link BiometricPrompt.Builder#setNegativeButton(CharSequence, Executor,
+     * DialogInterface.OnClickListener)}.
      * @hide
      */
     public static final int DISMISSED_REASON_NEGATIVE = 2;
@@ -112,6 +115,25 @@
      */
     public static final int DISMISSED_REASON_USER_CANCEL = 3;
 
+    /**
+     * Authenticated, confirmation not required. Dialog animated away.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_CONFIRM_NOT_REQUIRED = 4;
+
+    /**
+     * Error message shown on SystemUI. When BiometricService receives this, the UI is already
+     * gone.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_ERROR = 5;
+
+    /**
+     * Dialog dismissal requested by BiometricService.
+     * @hide
+     */
+    public static final int DISMISSED_REASON_SERVER_REQUESTED = 6;
+
     private static class ButtonInfo {
         Executor executor;
         DialogInterface.OnClickListener listener;
@@ -362,7 +384,7 @@
         @Override
         public void onDialogDismissed(int reason) throws RemoteException {
             // Check the reason and invoke OnClickListener(s) if necessary
-            if (reason == DISMISSED_REASON_POSITIVE) {
+            if (reason == DISMISSED_REASON_CONFIRMED) {
                 mPositiveButtonInfo.executor.execute(() -> {
                     mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
                 });
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index 180daaf..ca6114e 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -27,8 +27,8 @@
     // Notify BiometricService that authentication was successful. If user confirmation is required,
     // the auth token must be submitted into KeyStore.
     void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
-    // Notify BiometricService that an error has occurred.
-    void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+    // Notify BiometricService authentication was rejected.
+    void onAuthenticationFailed();
     // Notify BiometricService than an error has occured. Forward to the correct receiver depending
     // on the cookie.
     void onError(int cookie, int error, String message);
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 0fb93e5..beff0f7 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -211,10 +211,12 @@
 
     @Override
     public void onProgramListUpdated(ProgramList.Chunk chunk) {
-        synchronized (mLock) {
-            if (mProgramList == null) return;
-            mProgramList.apply(Objects.requireNonNull(chunk));
-        }
+        mHandler.post(() -> {
+            synchronized (mLock) {
+                if (mProgramList == null) return;
+                mProgramList.apply(Objects.requireNonNull(chunk));
+            }
+        });
     }
 
     @Override
diff --git a/core/java/android/net/util/MultinetworkPolicyTracker.java b/core/java/android/net/util/MultinetworkPolicyTracker.java
index 30c5cd9..f7e494d 100644
--- a/core/java/android/net/util/MultinetworkPolicyTracker.java
+++ b/core/java/android/net/util/MultinetworkPolicyTracker.java
@@ -16,29 +16,32 @@
 
 package android.net.util;
 
+import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.database.ContentObserver;
-import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.os.Handler;
-import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.Slog;
 
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Arrays;
 import java.util.List;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.R;
-
-import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI;
-import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
-
 /**
  * A class to encapsulate management of the "Smart Networking" capability of
  * avoiding bad Wi-Fi when, for example upstream connectivity is lost or
@@ -69,6 +72,7 @@
 
     private volatile boolean mAvoidBadWifi = true;
     private volatile int mMeteredMultipathPreference;
+    private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     public MultinetworkPolicyTracker(Context ctx, Handler handler) {
         this(ctx, handler, null);
@@ -95,6 +99,14 @@
             }
         };
 
+        TelephonyManager.from(ctx).listen(new PhoneStateListener() {
+            @Override
+            public void onActiveDataSubscriptionIdChanged(int subId) {
+                mActiveSubId = subId;
+                reevaluate();
+            }
+        }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+
         updateAvoidBadWifi();
         updateMeteredMultipathPreference();
     }
@@ -131,7 +143,12 @@
      * Whether the device or carrier configuration disables avoiding bad wifi by default.
      */
     public boolean configRestrictsAvoidBadWifi() {
-        return (mContext.getResources().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+        return (getResourcesForActiveSubId().getInteger(R.integer.config_networkAvoidBadWifi) == 0);
+    }
+
+    @NonNull
+    private Resources getResourcesForActiveSubId() {
+        return SubscriptionManager.getResourcesForSubId(mContext, mActiveSubId);
     }
 
     /**
diff --git a/core/java/android/os/UserManagerInternal.java b/core/java/android/os/UserManagerInternal.java
index 2e3b000..f302263 100644
--- a/core/java/android/os/UserManagerInternal.java
+++ b/core/java/android/os/UserManagerInternal.java
@@ -16,6 +16,7 @@
 package android.os;
 
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
@@ -224,4 +225,10 @@
 
     /** @return a specific user restriction that's in effect currently. */
     public abstract boolean hasUserRestriction(String restriction, int userId);
+
+    /**
+     * Gets an {@link UserInfo} for the given {@code userId}, or {@code null} if not
+     * found.
+     */
+    public abstract @Nullable UserInfo getUserInfo(@UserIdInt int userId);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2159cf4..bfafa2f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,16 +16,19 @@
 
 package android.provider;
 
-import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
-import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
-import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
-import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
-import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.URI_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
 
 import android.Manifest;
 import android.annotation.IntDef;
@@ -80,7 +83,12 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.ComponentNameListValidator;
+import android.provider.settings.validators.DiscreteValueValidator;
+import android.provider.settings.validators.InclusiveFloatRangeValidator;
+import android.provider.settings.validators.InclusiveIntegerRangeValidator;
+import android.provider.settings.validators.PackageNameListValidator;
+import android.provider.settings.validators.Validator;
 import android.speech.tts.TextToSpeech;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
@@ -3149,7 +3157,7 @@
         public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
 
         private static final Validator END_BUTTON_BEHAVIOR_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * END_BUTTON_BEHAVIOR value for "go home".
@@ -3351,7 +3359,7 @@
             "bluetooth_discoverability";
 
         private static final Validator BLUETOOTH_DISCOVERABILITY_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Bluetooth discoverability timeout.  If this value is nonzero, then
@@ -3495,7 +3503,7 @@
         public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
 
         private static final Validator PEAK_REFRESH_RATE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
+                new InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
 
         /**
          * The amount of time in milliseconds before the device goes to sleep or begins
@@ -3524,7 +3532,7 @@
         public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
 
         private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 255);
+                new InclusiveIntegerRangeValidator(0, 255);
 
         /**
          * Control whether to enable automatic brightness mode.
@@ -3542,7 +3550,7 @@
         public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
 
         private static final Validator SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(-1, 1);
+                new InclusiveFloatRangeValidator(-1, 1);
 
         /**
          * SCREEN_BRIGHTNESS_MODE value for manual mode.
@@ -3676,7 +3684,7 @@
                 "haptic_feedback_intensity";
 
         private static final Validator VIBRATION_INTENSITY_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * Ringer volume. This is used internally, changing this value will not
@@ -3766,7 +3774,7 @@
         public static final String MASTER_BALANCE = "master_balance";
 
         private static final Validator MASTER_BALANCE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(-1.f, 1.f);
+                new InclusiveFloatRangeValidator(-1.f, 1.f);
 
         /**
          * Whether the notifications should use the ring volume (value of 1) or
@@ -4004,7 +4012,7 @@
 
         /** @hide */
         public static final Validator TIME_12_24_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"12", "24", null});
+                new DiscreteValueValidator(new String[] {"12", "24", null});
 
         /**
          * Date format string
@@ -4090,7 +4098,7 @@
 
         /** @hide */
         public static final Validator USER_ROTATION_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * Control whether the rotation lock toggle in the System UI should be hidden.
@@ -4179,7 +4187,7 @@
 
         /** @hide */
         public static final Validator TTY_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+                new InclusiveIntegerRangeValidator(0, 3);
 
         /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
@@ -4381,7 +4389,7 @@
 
         /** @hide */
         public static final Validator SIP_CALL_OPTIONS_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(
+                new DiscreteValueValidator(
                         new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"});
 
         /**
@@ -4428,7 +4436,7 @@
 
         /** @hide */
         public static final Validator POINTER_SPEED_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(-7, 7);
+                new InclusiveFloatRangeValidator(-7, 7);
 
         /**
          * Whether lock-to-app will be triggered by long-press on recents.
@@ -6352,7 +6360,7 @@
         public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
 
         private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR =
-                SettingsValidators.JSON_OBJECT_VALIDATOR;
+                JSON_OBJECT_VALIDATOR;
 
         /**
          * Indicates which clock face to show on lock screen and AOD while docked.
@@ -6509,7 +6517,7 @@
             "enabled_accessibility_services";
 
         private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * List of the accessibility services to which the user has granted
@@ -6521,7 +6529,7 @@
             "touch_exploration_granted_accessibility_services";
 
         private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Whether the Global Actions Panel is enabled.
@@ -6696,7 +6704,7 @@
                 "accessibility_display_magnification_scale";
 
         private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+                new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
 
         /**
          * Unused mangnification setting
@@ -6780,7 +6788,7 @@
                 "accessibility_captioning_preset";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+                new DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
                         "3", "4"});
 
         /**
@@ -6824,7 +6832,7 @@
                 "accessibility_captioning_edge_type";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+                new DiscreteValueValidator(new String[]{"0", "1", "2"});
 
         /**
          * Integer property that specifes the edge color for captions as a
@@ -6870,7 +6878,7 @@
                 "accessibility_captioning_typeface";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+                new DiscreteValueValidator(new String[]{"DEFAULT",
                         "MONOSPACE", "SANS_SERIF", "SERIF"});
 
         /**
@@ -6882,7 +6890,7 @@
                 "accessibility_captioning_font_scale";
 
         private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
-                new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+                new InclusiveFloatRangeValidator(0.5f, 2.0f);
 
         /**
          * Setting that specifies whether display color inversion is enabled.
@@ -6923,7 +6931,7 @@
                 "accessibility_display_daltonizer";
 
         private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(
+                new DiscreteValueValidator(
                         new String[] {"-1", "0", "11", "12", "13"});
 
         /**
@@ -7112,24 +7120,7 @@
          */
         public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
 
-        private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = new Validator() {
-            @Override
-            public boolean validate(@Nullable String value) {
-                if (value == null || value.length() == 0) {
-                    return false;
-                }
-                String[] ttsLocales = value.split(",");
-                boolean valid = true;
-                for (String ttsLocale : ttsLocales) {
-                    String[] parts = ttsLocale.split(":");
-                    valid |= ((parts.length == 2)
-                            && (parts[0].length() > 0)
-                            && ANY_STRING_VALIDATOR.validate(parts[0])
-                            && LOCALE_VALIDATOR.validate(parts[1]));
-                }
-                return valid;
-            }
-        };
+        private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = TTS_LIST_VALIDATOR;
 
         /**
          * Space delimited list of plugin packages that are enabled.
@@ -7137,7 +7128,7 @@
         public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
 
         private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
-                new SettingsValidators.PackageNameListValidator(" ");
+                new PackageNameListValidator(" ");
 
         /**
          * @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
@@ -7331,7 +7322,7 @@
                 "preferred_tty_mode";
 
         private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+                new DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
 
         /**
          * Whether the enhanced voice privacy mode is enabled.
@@ -7646,7 +7637,7 @@
         public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
 
         private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+                new DiscreteValueValidator(new String[]{"1", "2"});
 
         /**
          * INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
@@ -7867,7 +7858,7 @@
         public static final String UI_NIGHT_MODE = "ui_night_mode";
 
         private static final Validator UI_NIGHT_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Whether screensavers are enabled.
@@ -7887,7 +7878,7 @@
         public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
 
         private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(",");
+                new ComponentNameListValidator(",");
 
         /**
          * If screensavers are enabled, whether the screensaver should be automatically launched
@@ -8039,7 +8030,7 @@
                 "enabled_notification_assistant";
 
         private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Read only list of the service components that the current user has explicitly allowed to
@@ -8054,7 +8045,7 @@
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
         private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Read only list of the packages that the current user has explicitly allowed to
@@ -8069,7 +8060,7 @@
                 "enabled_notification_policy_access_packages";
 
         private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
-                new SettingsValidators.PackageNameListValidator(":");
+                new PackageNameListValidator(":");
 
         /**
          * Defines whether managed profile ringtones should be synced from it's parent profile
@@ -8366,16 +8357,6 @@
                 BOOLEAN_VALIDATOR;
 
         /**
-         * Whether or not the face unlock education screen has been shown to the user.
-         * @hide
-         */
-        public static final String FACE_UNLOCK_EDUCATION_INFO_DISPLAYED =
-                "face_unlock_education_info_displayed";
-
-        private static final Validator FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR =
-                BOOLEAN_VALIDATOR;
-
-        /**
          * Whether or not debugging is enabled.
          * @hide
          */
@@ -8462,7 +8443,7 @@
         public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
 
         private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+                new InclusiveIntegerRangeValidator(0, 2);
 
         /**
          * Control the color temperature of Night Display, represented in Kelvin.
@@ -8523,7 +8504,7 @@
         public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
 
         private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
-                new SettingsValidators.ComponentNameListValidator(":");
+                new ComponentNameListValidator(":");
 
         /**
          * Behavior of the display while in VR mode.
@@ -8535,7 +8516,7 @@
         public static final String VR_DISPLAY_MODE = "vr_display_mode";
 
         private static final Validator VR_DISPLAY_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+                new DiscreteValueValidator(new String[]{"0", "1"});
 
         /**
          * Lower the display persistence while the system is in VR mode.
@@ -8649,25 +8630,12 @@
 
         /**
          * Holds comma separated list of ordering of QS tiles.
+         *
          * @hide
          */
         public static final String QS_TILES = "sysui_qs_tiles";
 
-        private static final Validator QS_TILES_VALIDATOR = new Validator() {
-            @Override
-            public boolean validate(@Nullable String value) {
-                if (value == null) {
-                    return false;
-                }
-                String[] tiles = value.split(",");
-                boolean valid = true;
-                for (String tile : tiles) {
-                    // tile can be any non-empty string as specified by OEM
-                    valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
-                }
-                return valid;
-            }
-        };
+        private static final Validator QS_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
 
         /**
          * Specifies whether the web action API is enabled.
@@ -8733,21 +8701,7 @@
          */
         public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
 
-        private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = new Validator() {
-            @Override
-            public boolean validate(@Nullable String value) {
-                if (value == null) {
-                    return false;
-                }
-                String[] tiles = value.split(",");
-                boolean valid = true;
-                for (String tile : tiles) {
-                    // tile can be any non-empty string as specified by OEM
-                    valid |= ((tile.length() > 0) && ANY_STRING_VALIDATOR.validate(tile));
-                }
-                return valid;
-            }
-        };
+        private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
 
         /**
          * Whether the Lockdown button should be shown in the power menu.
@@ -8908,7 +8862,7 @@
                 "theme_customization_overlay_packages";
 
         private static final Validator THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR =
-                SettingsValidators.JSON_OBJECT_VALIDATOR;
+                JSON_OBJECT_VALIDATOR;
 
         /**
          * Navigation bar mode.
@@ -8920,7 +8874,7 @@
         public static final String NAVIGATION_MODE =
                 "navigation_mode";
         private static final Validator NAVIGATION_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+                new DiscreteValueValidator(new String[] {"0", "1", "2"});
 
         /**
          * Controls whether aware is enabled.
@@ -9223,8 +9177,6 @@
             VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
             VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
                     FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
-            VALIDATORS.put(FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
-                    FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
                     ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -10774,7 +10726,7 @@
                 "network_recommendations_enabled";
 
         private static final Validator NETWORK_RECOMMENDATIONS_ENABLED_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "1"});
+                new DiscreteValueValidator(new String[] {"-1", "0", "1"});
 
         /**
          * Which package name to use for network recommendations. If null, network recommendations
@@ -11099,12 +11051,6 @@
        public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
 
        /**
-        * The min time between wifi disable and wifi enable
-        * @hide
-        */
-       public static final String WIFI_REENABLE_DELAY_MS = "wifi_reenable_delay";
-
-       /**
         * Timeout for ephemeral networks when all known BSSIDs go out of range. We will disconnect
         * from an ephemeral network if there is no BSSID for that network with a non-null score that
         * has been seen in this time period.
@@ -12631,7 +12577,7 @@
         public static final String EMERGENCY_TONE = "emergency_tone";
 
         private static final Validator EMERGENCY_TONE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+                new DiscreteValueValidator(new String[] {"0", "1", "2"});
 
         /**
          * CDMA only settings
@@ -12661,7 +12607,7 @@
                 "enable_automatic_system_server_heap_dumps";
 
         private static final Validator ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+                new DiscreteValueValidator(new String[] {"0", "1"});
 
         /**
          * See RIL_PreferredNetworkType in ril.h
@@ -12855,7 +12801,7 @@
                 "low_power_sticky_auto_disable_level";
 
         private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+                new InclusiveIntegerRangeValidator(0, 100);
 
         /**
          * Whether sticky battery saver should be deactivated once the battery level has reached the
@@ -12867,7 +12813,7 @@
                 "low_power_sticky_auto_disable_enabled";
 
         private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+                new DiscreteValueValidator(new String[] {"0", "1"});
 
         /**
          * Battery level [1-100] at which low power mode automatically turns on.
@@ -12882,7 +12828,7 @@
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
 
         private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+                new InclusiveIntegerRangeValidator(0, 100);
 
         /**
          * Whether battery saver is currently set to trigger based on percentage, dynamic power
@@ -12895,7 +12841,7 @@
         public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
 
         private static final Validator AUTOMATIC_POWER_SAVE_MODE_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+                new DiscreteValueValidator(new String[] {"0", "1"});
 
         /**
          * The setting that backs the disable threshold for the setPowerSavingsWarning api in
@@ -12908,7 +12854,7 @@
         public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
                 "dynamic_power_savings_disable_threshold";
         private static final Validator DYNAMIC_POWER_SAVINGS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+                new InclusiveIntegerRangeValidator(0, 100);
 
         /**
          * The setting which backs the setDynamicPowerSaveHint api in PowerManager.
@@ -13058,7 +13004,7 @@
         public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
 
         private static final Validator ENCODED_SURROUND_OUTPUT_VALIDATOR =
-                new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
+                new DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
 
         /**
          * Surround sounds formats that are enabled when ENCODED_SURROUND_OUTPUT is set to
@@ -13809,7 +13755,7 @@
         public static final String POWER_BUTTON_LONG_PRESS =
                 "power_button_long_press";
         private static final Validator POWER_BUTTON_LONG_PRESS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+                new InclusiveIntegerRangeValidator(0, 5);
 
         /**
          * Overrides internal R.integer.config_veryLongPressOnPowerBehavior.
@@ -13820,7 +13766,7 @@
         public static final String POWER_BUTTON_VERY_LONG_PRESS =
                 "power_button_very_long_press";
         private static final Validator POWER_BUTTON_VERY_LONG_PRESS_VALIDATOR =
-                new SettingsValidators.InclusiveIntegerRangeValidator(0, 1);
+                new InclusiveIntegerRangeValidator(0, 1);
 
         /**
          * Settings to backup. This is here so that it's in the same place as the settings
diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING
new file mode 100644
index 0000000..52a6a45
--- /dev/null
+++ b/core/java/android/provider/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+    "presubmit": [
+        {
+            "name": "CtsProviderTestCases",
+            "options": [
+                {
+                    "include-annotation": "android.platform.test.annotations.Presubmit"
+                }
+            ]
+        }
+    ]
+}
diff --git a/core/java/android/provider/settings/validators/ComponentNameListValidator.java b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
new file mode 100644
index 0000000..b6b867a
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+
+/**
+ * Validate a list of compoments.
+ *
+ * @hide
+ */
+public final class ComponentNameListValidator extends ListValidator {
+    public ComponentNameListValidator(String separator) {
+        super(separator);
+    }
+
+    @Override
+    protected boolean isEntryValid(String entry) {
+        return entry != null;
+    }
+
+    @Override
+    protected boolean isItemValid(String item) {
+        return COMPONENT_NAME_VALIDATOR.validate(item);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/DiscreteValueValidator.java b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
new file mode 100644
index 0000000..183651f
--- /dev/null
+++ b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import android.annotation.Nullable;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Validate a value exists in an array of known good values
+ *
+ * @hide
+ */
+public final class DiscreteValueValidator implements Validator {
+    private final String[] mValues;
+
+    public DiscreteValueValidator(String[] values) {
+        mValues = values;
+    }
+
+    @Override
+    public boolean validate(@Nullable String value) {
+        return ArrayUtils.contains(mValues, value);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
new file mode 100644
index 0000000..38400ac
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate a float value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveFloatRangeValidator implements Validator {
+    private final float mMin;
+    private final float mMax;
+
+    public InclusiveFloatRangeValidator(float min, float max) {
+        mMin = min;
+        mMax = max;
+    }
+
+    @Override
+    public boolean validate(@Nullable String value) {
+        try {
+            final float floatValue = Float.parseFloat(value);
+            return floatValue >= mMin && floatValue <= mMax;
+        } catch (NumberFormatException | NullPointerException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
new file mode 100644
index 0000000..e53c252
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate an integer value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveIntegerRangeValidator implements Validator {
+    private final int mMin;
+    private final int mMax;
+
+    public InclusiveIntegerRangeValidator(int min, int max) {
+        mMin = min;
+        mMax = max;
+    }
+
+    @Override
+    public boolean validate(@Nullable String value) {
+        try {
+            final int intValue = Integer.parseInt(value);
+            return intValue >= mMin && intValue <= mMax;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+}
diff --git a/core/java/android/provider/settings/validators/ListValidator.java b/core/java/android/provider/settings/validators/ListValidator.java
new file mode 100644
index 0000000..a6001d2
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ListValidator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate the elements in a list.
+ *
+ * @hide
+ */
+abstract class ListValidator implements Validator {
+
+    private String mListSplitRegex;
+
+    ListValidator(String listSplitRegex) {
+        mListSplitRegex = listSplitRegex;
+    }
+
+    public boolean validate(@Nullable String value) {
+        if (!isEntryValid(value)) {
+            return false;
+        }
+        String[] items = value.split(",");
+        for (String item : items) {
+            if (!isItemValid(item)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    protected abstract boolean isEntryValid(String entry);
+
+    protected abstract boolean isItemValid(String item);
+}
+
diff --git a/core/java/android/provider/settings/validators/PackageNameListValidator.java b/core/java/android/provider/settings/validators/PackageNameListValidator.java
new file mode 100644
index 0000000..bc7fc13
--- /dev/null
+++ b/core/java/android/provider/settings/validators/PackageNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+
+/**
+ * Validate a list of package names.
+ *
+ * @hide
+ */
+public final class PackageNameListValidator extends ListValidator {
+    public PackageNameListValidator(String separator) {
+        super(separator);
+    }
+
+    @Override
+    protected boolean isEntryValid(String entry) {
+        return entry != null;
+    }
+
+    @Override
+    protected boolean isItemValid(String item) {
+        return PACKAGE_NAME_VALIDATOR.validate(item);
+    }
+}
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/settings/validators/SettingsValidators.java
similarity index 64%
rename from core/java/android/provider/SettingsValidators.java
rename to core/java/android/provider/settings/validators/SettingsValidators.java
index 4051213..562c638 100644
--- a/core/java/android/provider/SettingsValidators.java
+++ b/core/java/android/provider/settings/validators/SettingsValidators.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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,15 +14,13 @@
  * limitations under the License.
  */
 
-package android.provider;
+package android.provider.settings.validators;
 
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.net.Uri;
 import android.text.TextUtils;
 
-import com.android.internal.util.ArrayUtils;
-
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -179,108 +177,7 @@
         }
     };
 
-    public interface Validator {
-        /**
-         * Returns whether the input value is valid. Subclasses should handle the case where the
-         * input value is {@code null}.
-         */
-        boolean validate(@Nullable String value);
-    }
+    public static final Validator TTS_LIST_VALIDATOR = new TTSListValidator();
 
-    public static final class DiscreteValueValidator implements Validator {
-        private final String[] mValues;
-
-        public DiscreteValueValidator(String[] values) {
-            mValues = values;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            return ArrayUtils.contains(mValues, value);
-        }
-    }
-
-    public static final class InclusiveIntegerRangeValidator implements Validator {
-        private final int mMin;
-        private final int mMax;
-
-        public InclusiveIntegerRangeValidator(int min, int max) {
-            mMin = min;
-            mMax = max;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                final int intValue = Integer.parseInt(value);
-                return intValue >= mMin && intValue <= mMax;
-            } catch (NumberFormatException e) {
-                return false;
-            }
-        }
-    }
-
-    public static final class InclusiveFloatRangeValidator implements Validator {
-        private final float mMin;
-        private final float mMax;
-
-        public InclusiveFloatRangeValidator(float min, float max) {
-            mMin = min;
-            mMax = max;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            try {
-                final float floatValue = Float.parseFloat(value);
-                return floatValue >= mMin && floatValue <= mMax;
-            } catch (NumberFormatException | NullPointerException e) {
-                return false;
-            }
-        }
-    }
-
-    public static final class ComponentNameListValidator implements Validator {
-        private final String mSeparator;
-
-        public ComponentNameListValidator(String separator) {
-            mSeparator = separator;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            if (value == null) {
-                return false;
-            }
-            String[] elements = value.split(mSeparator);
-            for (String element : elements) {
-                if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
-    public static final class PackageNameListValidator implements Validator {
-        private final String mSeparator;
-
-        public PackageNameListValidator(String separator) {
-            mSeparator = separator;
-        }
-
-        @Override
-        public boolean validate(@Nullable String value) {
-            if (value == null) {
-                return false;
-            }
-            String[] elements = value.split(mSeparator);
-            for (String element : elements) {
-                if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
+    public static final Validator TILE_LIST_VALIDATOR = new TileListValidator();
 }
diff --git a/core/java/android/provider/settings/validators/TTSListValidator.java b/core/java/android/provider/settings/validators/TTSListValidator.java
new file mode 100644
index 0000000..6c73471
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TTSListValidator.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+
+/**
+ * Ensure a restored value is a string in the format the text-to-speech system handles
+ *
+ * @hide
+ */
+final class TTSListValidator extends ListValidator {
+
+    TTSListValidator() {
+        super(",");
+    }
+
+    protected boolean isEntryValid(String entry) {
+        return entry != null && entry.length() > 0;
+    }
+
+    protected boolean isItemValid(String item) {
+        String[] parts = item.split(":");
+        // Replaces any old language separator (-) with the new one (_)
+        return ((parts.length == 2)
+                && (parts[0].length() > 0)
+                && ANY_STRING_VALIDATOR.validate(parts[0])
+                && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
+    }
+}
diff --git a/core/java/android/provider/settings/validators/TileListValidator.java b/core/java/android/provider/settings/validators/TileListValidator.java
new file mode 100644
index 0000000..c696442
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TileListValidator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+
+/**
+ * Ensure a restored value is suitable to be used as a tile name
+ *
+ * @hide
+ */
+final class TileListValidator extends ListValidator {
+    TileListValidator() {
+        super(",");
+    }
+
+    protected boolean isEntryValid(String entry) {
+        return entry != null;
+    }
+
+    protected boolean isItemValid(String item) {
+        return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
+    }
+}
diff --git a/core/java/android/provider/settings/validators/Validator.java b/core/java/android/provider/settings/validators/Validator.java
new file mode 100644
index 0000000..393a03d
--- /dev/null
+++ b/core/java/android/provider/settings/validators/Validator.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for a settings value validator.
+ *
+ * @hide
+ */
+public interface Validator {
+    /**
+     * Returns whether the input value is valid. Subclasses should handle the case where the
+     * input value is {@code null}.
+     */
+    boolean validate(@Nullable String value);
+}
diff --git a/core/java/android/service/gatekeeper/GateKeeperResponse.java b/core/java/android/service/gatekeeper/GateKeeperResponse.java
index 66fee1e..7ed733c 100644
--- a/core/java/android/service/gatekeeper/GateKeeperResponse.java
+++ b/core/java/android/service/gatekeeper/GateKeeperResponse.java
@@ -31,6 +31,8 @@
     public static final int RESPONSE_OK = 0;
     public static final int RESPONSE_RETRY = 1;
 
+    public static final GateKeeperResponse ERROR = createGenericResponse(RESPONSE_ERROR);
+
     private final int mResponseCode;
 
     private int mTimeout;
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 70d049a..fe536a6 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import libcore.util.FP16;
+
 /**
  * <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
  * <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -148,25 +150,6 @@
      */
     public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000;
 
-    private static final int FP16_SIGN_SHIFT        = 15;
-    private static final int FP16_SIGN_MASK         = 0x8000;
-    private static final int FP16_EXPONENT_SHIFT    = 10;
-    private static final int FP16_EXPONENT_MASK     = 0x1f;
-    private static final int FP16_SIGNIFICAND_MASK  = 0x3ff;
-    private static final int FP16_EXPONENT_BIAS     = 15;
-    private static final int FP16_COMBINED          = 0x7fff;
-    private static final int FP16_EXPONENT_MAX      = 0x7c00;
-
-    private static final int FP32_SIGN_SHIFT        = 31;
-    private static final int FP32_EXPONENT_SHIFT    = 23;
-    private static final int FP32_EXPONENT_MASK     = 0xff;
-    private static final int FP32_SIGNIFICAND_MASK  = 0x7fffff;
-    private static final int FP32_EXPONENT_BIAS     = 127;
-    private static final int FP32_QNAN_MASK         = 0x400000;
-
-    private static final int FP32_DENORMAL_MAGIC = 126 << 23;
-    private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
-
     private final @HalfFloat short mValue;
 
     /**
@@ -414,16 +397,7 @@
      *          than {@code y}
      */
     public static int compare(@HalfFloat short x, @HalfFloat short y) {
-        if (less(x, y)) return -1;
-        if (greater(x, y)) return 1;
-
-        // Collapse NaNs, akin to halfToIntBits(), but we want to keep
-        // (signed) short value types to preserve the ordering of -0.0
-        // and +0.0
-        short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x;
-        short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y;
-
-        return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+        return FP16.compare(x, y);
     }
 
     /**
@@ -440,7 +414,7 @@
      * @see #halfToIntBits(short)
      */
     public static @HalfFloat short halfToShortBits(@HalfFloat short h) {
-        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h;
+        return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h;
     }
 
     /**
@@ -459,7 +433,7 @@
      * @see #intBitsToHalf(int)
      */
     public static int halfToIntBits(@HalfFloat short h) {
-        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff;
+        return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h & 0xffff;
     }
 
     /**
@@ -505,7 +479,7 @@
      *         of the second parameter
      */
     public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
-        return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED));
+        return (short) ((sign & FP16.SIGN_MASK) | (magnitude & FP16.EXPONENT_SIGNIFICAND_MASK));
     }
 
     /**
@@ -523,7 +497,7 @@
      * @return The absolute value of the specified half-precision float
      */
     public static @HalfFloat short abs(@HalfFloat short h) {
-        return (short) (h & FP16_COMBINED);
+        return (short) (h & FP16.EXPONENT_SIGNIFICAND_MASK);
     }
 
     /**
@@ -538,26 +512,18 @@
      * the result is zero (with the same sign)</li>
      * </ul>
      *
+     * <p class=note>
+     * <strong>Note:</strong> Unlike the identically named
+     * <code class=prettyprint>int java.lang.Math.round(float)</code> method,
+     * this returns a Half value stored in a short, <strong>not</strong> an
+     * actual short integer result.
+     *
      * @param h A half-precision float value
      * @return The value of the specified half-precision float rounded to the nearest
      *         half-precision float value
      */
     public static @HalfFloat short round(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-            result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0));
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result += (1 << (e - 1));
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.rint(h);
     }
 
     /**
@@ -577,21 +543,7 @@
      *         greater than or equal to the specified half-precision float value
      */
     public static @HalfFloat short ceil(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-            result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0));
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result += mask & ((bits >> 15) - 1);
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.ceil(h);
     }
 
     /**
@@ -611,21 +563,7 @@
      *         less than or equal to the specified half-precision float value
      */
     public static @HalfFloat short floor(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-            result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result += mask & -(bits >> 15);
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.floor(h);
     }
 
     /**
@@ -644,19 +582,7 @@
      *         half-precision float value
      */
     public static @HalfFloat short trunc(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int e = bits & 0x7fff;
-        int result = bits;
-
-        if (e < 0x3c00) {
-            result &= FP16_SIGN_MASK;
-        } else if (e < 0x6400) {
-            e = 25 - (e >> 10);
-            int mask = (1 << e) - 1;
-            result &= ~mask;
-        }
-
-        return (short) result;
+        return FP16.trunc(h);
     }
 
     /**
@@ -672,15 +598,7 @@
      * @return The smaller of the two specified half-precision values
      */
     public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
-        if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
-            return (x & FP16_SIGN_MASK) != 0 ? x : y;
-        }
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+        return FP16.min(x, y);
     }
 
     /**
@@ -697,15 +615,7 @@
      * @return The larger of the two specified half-precision values
      */
     public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
-        if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
-            return (x & FP16_SIGN_MASK) != 0 ? y : x;
-        }
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+        return FP16.max(x, y);
     }
 
     /**
@@ -719,11 +629,7 @@
      * @return True if x is less than y, false otherwise
      */
     public static boolean less(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.less(x, y);
     }
 
     /**
@@ -737,11 +643,7 @@
      * @return True if x is less than or equal to y, false otherwise
      */
     public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.lessEquals(x, y);
     }
 
     /**
@@ -755,11 +657,7 @@
      * @return True if x is greater than y, false otherwise
      */
     public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.greater(x, y);
     }
 
     /**
@@ -773,11 +671,7 @@
      * @return True if x is greater than y, false otherwise
      */
     public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
-               ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+        return FP16.greaterEquals(x, y);
     }
 
     /**
@@ -791,10 +685,7 @@
      * @return True if x is equal to y, false otherwise
      */
     public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
-        if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-        if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
-        return x == y || ((x | y) & FP16_COMBINED) == 0;
+        return FP16.equals(x, y);
     }
 
     /**
@@ -804,7 +695,7 @@
      * @return 1 if the value is positive, -1 if the value is negative
      */
     public static int getSign(@HalfFloat short h) {
-        return (h & FP16_SIGN_MASK) == 0 ? 1 : -1;
+        return (h & FP16.SIGN_MASK) == 0 ? 1 : -1;
     }
 
     /**
@@ -818,7 +709,7 @@
      * @return The unbiased exponent of the specified value
      */
     public static int getExponent(@HalfFloat short h) {
-        return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
+        return ((h >>> FP16.EXPONENT_SHIFT) & FP16.SHIFTED_EXPONENT_MASK) - FP16.EXPONENT_BIAS;
     }
 
     /**
@@ -829,7 +720,7 @@
      * @return The significand, or significand, of the specified vlaue
      */
     public static int getSignificand(@HalfFloat short h) {
-        return h & FP16_SIGNIFICAND_MASK;
+        return h & FP16.SIGNIFICAND_MASK;
     }
 
     /**
@@ -841,7 +732,7 @@
      *         false otherwise
      */
     public static boolean isInfinite(@HalfFloat short h) {
-        return (h & FP16_COMBINED) == FP16_EXPONENT_MAX;
+        return FP16.isInfinite(h);
     }
 
     /**
@@ -852,7 +743,7 @@
      * @return True if the value is a NaN, false otherwise
      */
     public static boolean isNaN(@HalfFloat short h) {
-        return (h & FP16_COMBINED) > FP16_EXPONENT_MAX;
+        return FP16.isNaN(h);
     }
 
     /**
@@ -866,7 +757,7 @@
      * @return True if the value is normalized, false otherwise
      */
     public static boolean isNormalized(@HalfFloat short h) {
-        return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX;
+        return FP16.isNormalized(h);
     }
 
     /**
@@ -885,35 +776,7 @@
      * @return A normalized single-precision float value
      */
     public static float toFloat(@HalfFloat short h) {
-        int bits = h & 0xffff;
-        int s = bits & FP16_SIGN_MASK;
-        int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
-        int m = (bits                        ) & FP16_SIGNIFICAND_MASK;
-
-        int outE = 0;
-        int outM = 0;
-
-        if (e == 0) { // Denormal or 0
-            if (m != 0) {
-                // Convert denorm fp16 into normalized fp32
-                float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
-                o -= FP32_DENORMAL_FLOAT;
-                return s == 0 ? o : -o;
-            }
-        } else {
-            outM = m << 13;
-            if (e == 0x1f) { // Infinite or NaN
-                outE = 0xff;
-                if (outM != 0) { // SNaNs are quieted
-                    outM |= FP32_QNAN_MASK;
-                }
-            } else {
-                outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
-            }
-        }
-
-        int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
-        return Float.intBitsToFloat(out);
+        return FP16.toFloat(h);
     }
 
     /**
@@ -940,44 +803,7 @@
      */
     @SuppressWarnings("StatementWithEmptyBody")
     public static @HalfFloat short toHalf(float f) {
-        int bits = Float.floatToRawIntBits(f);
-        int s = (bits >>> FP32_SIGN_SHIFT    );
-        int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK;
-        int m = (bits                        ) & FP32_SIGNIFICAND_MASK;
-
-        int outE = 0;
-        int outM = 0;
-
-        if (e == 0xff) { // Infinite or NaN
-            outE = 0x1f;
-            outM = m != 0 ? 0x200 : 0;
-        } else {
-            e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS;
-            if (e >= 0x1f) { // Overflow
-                outE = 0x31;
-            } else if (e <= 0) { // Underflow
-                if (e < -10) {
-                    // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
-                } else {
-                    // The fp32 value is a normalized float less than MIN_NORMAL,
-                    // we convert to a denorm fp16
-                    m = (m | 0x800000) >> (1 - e);
-                    if ((m & 0x1000) != 0) m += 0x2000;
-                    outM = m >> 13;
-                }
-            } else {
-                outE = e;
-                outM = m >> 13;
-                if ((m & 0x1000) != 0) {
-                    // Round to nearest "0.5" up
-                    int out = (outE << FP16_EXPONENT_SHIFT) | outM;
-                    out++;
-                    return (short) (out | (s << FP16_SIGN_SHIFT));
-                }
-            }
-        }
-
-        return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM);
+        return FP16.toHalf(f);
     }
 
     /**
@@ -1073,40 +899,6 @@
      */
     @NonNull
     public static String toHexString(@HalfFloat short h) {
-        StringBuilder o = new StringBuilder();
-
-        int bits = h & 0xffff;
-        int s = (bits >>> FP16_SIGN_SHIFT    );
-        int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
-        int m = (bits                        ) & FP16_SIGNIFICAND_MASK;
-
-        if (e == 0x1f) { // Infinite or NaN
-            if (m == 0) {
-                if (s != 0) o.append('-');
-                o.append("Infinity");
-            } else {
-                o.append("NaN");
-            }
-        } else {
-            if (s == 1) o.append('-');
-            if (e == 0) {
-                if (m == 0) {
-                    o.append("0x0.0p0");
-                } else {
-                    o.append("0x0.");
-                    String significand = Integer.toHexString(m);
-                    o.append(significand.replaceFirst("0{2,}$", ""));
-                    o.append("p-14");
-                }
-            } else {
-                o.append("0x1.");
-                String significand = Integer.toHexString(m);
-                o.append(significand.replaceFirst("0{2,}$", ""));
-                o.append('p');
-                o.append(Integer.toString(e - FP16_EXPONENT_BIAS));
-            }
-        }
-
-        return o.toString();
+        return FP16.toHexString(h);
     }
 }
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 8b5659b..3a1df3e 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,9 +60,19 @@
     }
 
     public synchronized void dump(PrintWriter pw) {
+        dump("", pw);
+    }
+
+    /**
+     * Dumps the content of local log to print writer with each log entry predeced with indent
+     *
+     * @param indent indent that precedes each log entry
+     * @param pw printer writer to write into
+     */
+    public synchronized void dump(String indent, PrintWriter pw) {
         Iterator<String> itr = mLog.iterator();
         while (itr.hasNext()) {
-            pw.println(itr.next());
+            pw.printf("%s%s\n", indent, itr.next());
         }
     }
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 85e9e49..fe88a91 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2550,12 +2550,10 @@
             return;
         }
 
-        final boolean isItemEnabled;
+        boolean isItemEnabled = view.isEnabled() && isEnabled();
         final ViewGroup.LayoutParams lp = view.getLayoutParams();
         if (lp instanceof AbsListView.LayoutParams) {
-            isItemEnabled = ((AbsListView.LayoutParams) lp).isEnabled && isEnabled();
-        } else {
-            isItemEnabled = false;
+            isItemEnabled &= ((AbsListView.LayoutParams) lp).isEnabled;
         }
 
         info.setEnabled(isItemEnabled);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 00206fc..faeecda 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -164,6 +164,8 @@
     public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
     private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
     public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
+
+    private boolean mIsAppPredictorComponentAvailable;
     private AppPredictor mAppPredictor;
     private AppPredictor.Callback mAppPredictorCallback;
     private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
@@ -617,6 +619,9 @@
                 .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
                 .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
 
+        // This is the only place this value is being set. Effectively final.
+        mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
+
         AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
         if (appPredictor != null) {
             mDirectShareAppTargetCache = new HashMap<>();
@@ -707,6 +712,32 @@
     }
 
     /**
+     * Returns true if app prediction service is defined and the component exists on device.
+     */
+    private boolean isAppPredictionServiceAvailable() {
+        final String appPredictionServiceName =
+                getString(R.string.config_defaultAppPredictionService);
+        if (appPredictionServiceName == null) {
+            return false;
+        }
+        final ComponentName appPredictionComponentName =
+                ComponentName.unflattenFromString(appPredictionServiceName);
+        if (appPredictionComponentName == null) {
+            return false;
+        }
+
+        // Check if the app prediction component actually exists on the device.
+        Intent intent = new Intent();
+        intent.setComponent(appPredictionComponentName);
+        if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
+            Log.e(TAG, "App prediction service is defined, but does not exist: "
+                    + appPredictionServiceName);
+            return false;
+        }
+        return true;
+    }
+
+    /**
      * Check if the profile currently used is a work profile.
      * @return true if it is work profile, false if it is parent profile (or no work profile is
      * set up)
@@ -1693,6 +1724,9 @@
 
     @Nullable
     private AppPredictor getAppPredictor() {
+        if (!mIsAppPredictorComponentAvailable) {
+            return null;
+        }
         if (mAppPredictor == null
                     && getPackageManager().getAppPredictionServicePackageName() != null) {
             final IntentFilter filter = getTargetIntentFilter();
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 1d81c59..152d699 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -35,7 +35,7 @@
 import android.os.storage.StorageVolume;
 import android.os.storage.VolumeInfo;
 import android.provider.Settings;
-import android.util.ArraySet;
+import android.util.ArrayMap;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -166,6 +166,23 @@
                 params.sizeBytes, testableInterface);
     }
 
+    private static boolean checkFitOnVolume(StorageManager storageManager, String volumePath,
+            SessionParams params) throws IOException {
+        if (volumePath == null) {
+            return false;
+        }
+        final int installFlags = translateAllocateFlags(params.installFlags);
+        final UUID target = storageManager.getUuidForPath(new File(volumePath));
+        final long availBytes = storageManager.getAllocatableBytes(target,
+                installFlags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY);
+        if (params.sizeBytes <= availBytes) {
+            return true;
+        }
+        final long cacheClearable = storageManager.getAllocatableBytes(target,
+                installFlags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY);
+        return params.sizeBytes <= availBytes + cacheClearable;
+    }
+
     @VisibleForTesting
     public static String resolveInstallVolume(Context context, SessionParams params,
             TestableInterface testInterface) throws IOException {
@@ -178,35 +195,23 @@
         ApplicationInfo existingInfo = testInterface.getExistingAppInfo(context,
                 params.appPackageName);
 
-        // Figure out best candidate volume, and also if we fit on internal
-        final ArraySet<String> allCandidates = new ArraySet<>();
-        boolean fitsOnInternal = false;
-        VolumeInfo bestCandidate = null;
-        long bestCandidateAvailBytes = Long.MIN_VALUE;
+        final ArrayMap<String, String> volumePaths = new ArrayMap<>();
+        String internalVolumePath = null;
         for (VolumeInfo vol : storageManager.getVolumes()) {
             if (vol.type == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) {
                 final boolean isInternalStorage = ID_PRIVATE_INTERNAL.equals(vol.id);
-                final UUID target = storageManager.getUuidForPath(new File(vol.path));
-                final long availBytes = storageManager.getAllocatableBytes(target,
-                        translateAllocateFlags(params.installFlags));
                 if (isInternalStorage) {
-                    fitsOnInternal = (params.sizeBytes <= availBytes);
+                    internalVolumePath = vol.path;
                 }
                 if (!isInternalStorage || allow3rdPartyOnInternal) {
-                    if (availBytes >= params.sizeBytes) {
-                        allCandidates.add(vol.fsUuid);
-                    }
-                    if (availBytes >= bestCandidateAvailBytes) {
-                        bestCandidate = vol;
-                        bestCandidateAvailBytes = availBytes;
-                    }
+                    volumePaths.put(vol.fsUuid, vol.path);
                 }
             }
         }
 
         // System apps always forced to internal storage
         if (existingInfo != null && existingInfo.isSystemApp()) {
-            if (fitsOnInternal) {
+            if (checkFitOnVolume(storageManager, internalVolumePath, params)) {
                 return StorageManager.UUID_PRIVATE_INTERNAL;
             } else {
                 throw new IOException("Not enough space on existing volume "
@@ -228,7 +233,7 @@
                 throw new IOException("Not allowed to install non-system apps on internal storage");
             }
 
-            if (fitsOnInternal) {
+            if (checkFitOnVolume(storageManager, internalVolumePath, params)) {
                 return StorageManager.UUID_PRIVATE_INTERNAL;
             } else {
                 throw new IOException("Requested internal only, but not enough space");
@@ -237,10 +242,14 @@
 
         // If app already exists somewhere, we must stay on that volume
         if (existingInfo != null) {
-            if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)
-                    && fitsOnInternal) {
-                return StorageManager.UUID_PRIVATE_INTERNAL;
-            } else if (allCandidates.contains(existingInfo.volumeUuid)) {
+            String existingVolumePath = null;
+            if (Objects.equals(existingInfo.volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                existingVolumePath = internalVolumePath;
+            } else if (volumePaths.containsKey(existingInfo.volumeUuid)) {
+                existingVolumePath = volumePaths.get(existingInfo.volumeUuid);
+            }
+
+            if (checkFitOnVolume(storageManager, existingVolumePath, params)) {
                 return existingInfo.volumeUuid;
             } else {
                 throw new IOException("Not enough space on existing volume "
@@ -250,12 +259,36 @@
 
         // We're left with new installations with either preferring external or auto, so just pick
         // volume with most space
-        if (bestCandidate != null) {
-            return bestCandidate.fsUuid;
+        if (volumePaths.size() == 1) {
+            if (checkFitOnVolume(storageManager, volumePaths.valueAt(0), params)) {
+                return volumePaths.keyAt(0);
+            }
         } else {
-            throw new IOException("No special requests, but no room on allowed volumes. "
-                + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal);
+            String bestCandidate = null;
+            long bestCandidateAvailBytes = Long.MIN_VALUE;
+            for (String vol : volumePaths.keySet()) {
+                final String volumePath = volumePaths.get(vol);
+                final UUID target = storageManager.getUuidForPath(new File(volumePath));
+
+                // We need to take into account freeable cached space, because we're choosing the
+                // best candidate amongst a list, not just checking if we fit at all.
+                final long availBytes = storageManager.getAllocatableBytes(target,
+                        translateAllocateFlags(params.installFlags));
+
+                if (availBytes >= bestCandidateAvailBytes) {
+                    bestCandidate = vol;
+                    bestCandidateAvailBytes = availBytes;
+                }
+            }
+
+            if (bestCandidateAvailBytes >= params.sizeBytes) {
+                return bestCandidate;
+            }
+
         }
+
+        throw new IOException("No special requests, but no room on allowed volumes. "
+                + " allow3rdPartyOnInternal? " + allow3rdPartyOnInternal);
     }
 
     public static boolean fitsOnInternal(Context context, SessionParams params) throws IOException {
diff --git a/core/java/com/android/internal/os/AtomicDirectory.java b/core/java/com/android/internal/os/AtomicDirectory.java
index ebcee15..8ca7a71 100644
--- a/core/java/com/android/internal/os/AtomicDirectory.java
+++ b/core/java/com/android/internal/os/AtomicDirectory.java
@@ -17,15 +17,17 @@
 package com.android.internal.os;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.FileUtils;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
-import libcore.io.IoUtils;
-
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
@@ -48,12 +50,13 @@
  * backing directory when checking existence, making changes, and deleting.
  */
 public final class AtomicDirectory {
-    private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>();
+
+    private static final String LOG_TAG = AtomicDirectory.class.getSimpleName();
+
     private final @NonNull File mBaseDirectory;
     private final @NonNull File mBackupDirectory;
 
-    private int mBaseDirectoryFd = -1;
-    private int mBackupDirectoryFd = -1;
+    private final @NonNull ArrayMap<File, FileOutputStream> mOpenFiles = new ArrayMap<>();
 
     /**
      * Creates a new instance.
@@ -67,15 +70,16 @@
     }
 
     /**
-     * Gets the backup directory if present. This could be useful if you are
-     * writing new state to the dir but need to access the last persisted state
-     * at the same time. This means that this call is useful in between
-     * {@link #startWrite()} and {@link #finishWrite()} or {@link #failWrite()}.
-     * You should not modify the content returned by this method.
+     * Gets the backup directory which may or may not exist. This could be
+     * useful if you are writing new state to the directory but need to access
+     * the last persisted state at the same time. This means that this call is
+     * useful in between {@link #startWrite()} and {@link #finishWrite()} or
+     * {@link #failWrite()}. You should not modify the content returned by this
+     * method.
      *
      * @see #startRead()
      */
-    public @Nullable File getBackupDirectory() {
+    public @NonNull File getBackupDirectory() {
         return mBackupDirectory;
     }
 
@@ -90,7 +94,8 @@
      */
     public @NonNull File startRead() throws IOException {
         restore();
-        return getOrCreateBaseDirectory();
+        ensureBaseDirectory();
+        return mBaseDirectory;
     }
 
     /**
@@ -99,10 +104,7 @@
      * @see #startRead()
      * @see #startWrite()
      */
-    public void finishRead() {
-        mBaseDirectoryFd = -1;
-        mBackupDirectoryFd = -1;
-    }
+    public void finishRead() {}
 
     /**
      * Starts editing this directory. After calling this method you should
@@ -121,7 +123,8 @@
      */
     public @NonNull File startWrite() throws IOException {
         backup();
-        return getOrCreateBaseDirectory();
+        ensureBaseDirectory();
+        return mBaseDirectory;
     }
 
     /**
@@ -135,11 +138,11 @@
      * @see #closeWrite(FileOutputStream)
      */
     public @NonNull FileOutputStream openWrite(@NonNull File file) throws IOException {
-        if (file.isDirectory() || !file.getParentFile().equals(getOrCreateBaseDirectory())) {
-            throw new IllegalArgumentException("Must be a file in " + getOrCreateBaseDirectory());
+        if (file.isDirectory() || !file.getParentFile().equals(mBaseDirectory)) {
+            throw new IllegalArgumentException("Must be a file in " + mBaseDirectory);
         }
         if (mOpenFiles.containsKey(file)) {
-            throw new IllegalArgumentException("Already open file" + file.getCanonicalPath());
+            throw new IllegalArgumentException("Already open file " + file.getAbsolutePath());
         }
         final FileOutputStream destination = new FileOutputStream(file);
         mOpenFiles.put(file, destination);
@@ -160,7 +163,7 @@
         }
         mOpenFiles.removeAt(indexOfValue);
         FileUtils.sync(destination);
-        IoUtils.closeQuietly(destination);
+        FileUtils.closeQuietly(destination);
     }
 
     public void failWrite(@NonNull FileOutputStream destination) {
@@ -169,7 +172,7 @@
             throw new IllegalArgumentException("Unknown file stream " + destination);
         }
         mOpenFiles.removeAt(indexOfValue);
-        IoUtils.closeQuietly(destination);
+        FileUtils.closeQuietly(destination);
     }
 
     /**
@@ -177,15 +180,15 @@
      *
      * @see #startWrite()
      *
-     * @throws IllegalStateException is some files are not closed.
+     * @throws IllegalStateException if some files are not closed.
      */
     public void finishWrite() {
         throwIfSomeFilesOpen();
-        fsyncDirectoryFd(mBaseDirectoryFd);
+
+        syncDirectory(mBaseDirectory);
+        syncParentDirectory();
         deleteDirectory(mBackupDirectory);
-        fsyncDirectoryFd(mBackupDirectoryFd);
-        mBaseDirectoryFd = -1;
-        mBackupDirectoryFd = -1;
+        syncParentDirectory();
     }
 
     /**
@@ -195,11 +198,12 @@
      */
     public void failWrite() {
         throwIfSomeFilesOpen();
+
         try{
             restore();
-        } catch (IOException ignored) {}
-        mBaseDirectoryFd = -1;
-        mBackupDirectoryFd = -1;
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Failed to restore in failWrite()", e);
+        }
     }
 
     /**
@@ -213,29 +217,28 @@
      * Deletes this directory.
      */
     public void delete() {
+        boolean deleted = false;
         if (mBaseDirectory.exists()) {
-            deleteDirectory(mBaseDirectory);
-            fsyncDirectoryFd(mBaseDirectoryFd);
+            deleted |= deleteDirectory(mBaseDirectory);
         }
         if (mBackupDirectory.exists()) {
-            deleteDirectory(mBackupDirectory);
-            fsyncDirectoryFd(mBackupDirectoryFd);
+            deleted |= deleteDirectory(mBackupDirectory);
+        }
+        if (deleted) {
+            syncParentDirectory();
         }
     }
 
-    private @NonNull File getOrCreateBaseDirectory() throws IOException {
-        if (!mBaseDirectory.exists()) {
-            if (!mBaseDirectory.mkdirs()) {
-                throw new IOException("Couldn't create directory " + mBaseDirectory);
-            }
-            FileUtils.setPermissions(mBaseDirectory.getPath(),
-                    FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH,
-                    -1, -1);
+    private void ensureBaseDirectory() throws IOException {
+        if (mBaseDirectory.exists()) {
+            return;
         }
-        if (mBaseDirectoryFd < 0) {
-            mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath());
+
+        if (!mBaseDirectory.mkdirs()) {
+            throw new IOException("Failed to create directory " + mBaseDirectory);
         }
-        return mBaseDirectory;
+        FileUtils.setPermissions(mBaseDirectory.getPath(),
+                FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
     }
 
     private void throwIfSomeFilesOpen() {
@@ -249,50 +252,56 @@
         if (!mBaseDirectory.exists()) {
             return;
         }
-        if (mBaseDirectoryFd < 0) {
-            mBaseDirectoryFd = getDirectoryFd(mBaseDirectory.getCanonicalPath());
-        }
+
         if (mBackupDirectory.exists()) {
             deleteDirectory(mBackupDirectory);
         }
         if (!mBaseDirectory.renameTo(mBackupDirectory)) {
-            throw new IOException("Couldn't backup " + mBaseDirectory
-                    + " to " + mBackupDirectory);
+            throw new IOException("Failed to backup " + mBaseDirectory + " to " + mBackupDirectory);
         }
-        mBackupDirectoryFd = mBaseDirectoryFd;
-        mBaseDirectoryFd = -1;
-        fsyncDirectoryFd(mBackupDirectoryFd);
+        syncParentDirectory();
     }
 
     private void restore() throws IOException {
         if (!mBackupDirectory.exists()) {
             return;
         }
-        if (mBackupDirectoryFd == -1) {
-            mBackupDirectoryFd = getDirectoryFd(mBackupDirectory.getCanonicalPath());
-        }
+
         if (mBaseDirectory.exists()) {
             deleteDirectory(mBaseDirectory);
         }
         if (!mBackupDirectory.renameTo(mBaseDirectory)) {
-            throw new IOException("Couldn't restore " + mBackupDirectory
-                    + " to " + mBaseDirectory);
+            throw new IOException("Failed to restore " + mBackupDirectory + " to "
+                    + mBaseDirectory);
         }
-        mBaseDirectoryFd = mBackupDirectoryFd;
-        mBackupDirectoryFd = -1;
-        fsyncDirectoryFd(mBaseDirectoryFd);
+        syncParentDirectory();
     }
 
-    private static void deleteDirectory(@NonNull File file) {
-        final File[] children = file.listFiles();
-        if (children != null) {
-            for (File child : children) {
-                deleteDirectory(child);
-            }
-        }
-        file.delete();
+    private static boolean deleteDirectory(@NonNull File directory) {
+        return FileUtils.deleteContentsAndDir(directory);
     }
 
-    private static native int getDirectoryFd(String path);
-    private static native void fsyncDirectoryFd(int fd);
+    private void syncParentDirectory() {
+        syncDirectory(mBaseDirectory.getParentFile());
+    }
+
+    // Standard Java IO doesn't allow opening a directory (will throw a FileNotFoundException
+    // instead), so we have to do it manually.
+    private static void syncDirectory(@NonNull File directory) {
+        String path = directory.getAbsolutePath();
+        FileDescriptor fd;
+        try {
+            fd = Os.open(path, OsConstants.O_RDONLY, 0);
+        } catch (ErrnoException e) {
+            Log.e(LOG_TAG, "Failed to open " + path, e);
+            return;
+        }
+        try {
+            Os.fsync(fd);
+        } catch (ErrnoException e) {
+            Log.e(LOG_TAG, "Failed to fsync " + path, e);
+        } finally {
+            FileUtils.closeQuietly(fd);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index cf0394d..9441825 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -153,7 +153,7 @@
 
     // Used to show the dialog when BiometricService starts authentication
     void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
-            boolean requireConfirmation, int userId);
+            boolean requireConfirmation, int userId, String opPackageName);
     // Used to hide the dialog when a biometric is authenticated
     void onBiometricAuthenticated(boolean authenticated, String failureReason);
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 85ae18e..4c3a177 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -101,7 +101,7 @@
 
     // Used to show the dialog when BiometricService starts authentication
     void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
-            boolean requireConfirmation, int userId);
+            boolean requireConfirmation, int userId, String opPackageName);
     // Used to hide the dialog when a biometric is authenticated
     void onBiometricAuthenticated(boolean authenticated, String failureReason);
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c73de8f..1bb2ba2 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -775,17 +775,54 @@
         return false;
     }
 
+    private boolean performAccessibilityActionCommon(int action) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+            case AccessibilityNodeInfo.ACTION_EXPAND:
+            case R.id.accessibilityActionScrollDown:
+                if (mCollapseOffset != 0) {
+                    smoothScrollTo(0, 0);
+                    return true;
+                }
+                break;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+            case R.id.accessibilityActionScrollUp:
+                if (mCollapseOffset < mCollapsibleHeight) {
+                    smoothScrollTo(mCollapsibleHeight, 0);
+                    return true;
+                } else if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
+                        && isDismissable()) {
+                    smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, 0);
+                    mDismissOnScrollerFinished = true;
+                    return true;
+                }
+                break;
+            case AccessibilityNodeInfo.ACTION_COLLAPSE:
+                if (mCollapseOffset < mCollapsibleHeight) {
+                    smoothScrollTo(mCollapsibleHeight, 0);
+                    return true;
+                }
+                break;
+            case AccessibilityNodeInfo.ACTION_DISMISS:
+                if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
+                        && isDismissable()) {
+                    smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, 0);
+                    mDismissOnScrollerFinished = true;
+                    return true;
+                }
+                break;
+        }
+
+        return false;
+    }
+
     @Override
     public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
         if (super.onNestedPrePerformAccessibilityAction(target, action, args)) {
             return true;
         }
 
-        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && mCollapseOffset != 0) {
-            smoothScrollTo(0, 0);
-            return true;
-        }
-        return false;
+        return performAccessibilityActionCommon(action);
     }
 
     @Override
@@ -802,9 +839,23 @@
 
         if (isEnabled()) {
             if (mCollapseOffset != 0) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
+                info.addAction(AccessibilityAction.ACTION_EXPAND);
+                info.addAction(AccessibilityAction.ACTION_SCROLL_DOWN);
                 info.setScrollable(true);
             }
+            if ((mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight)
+                    && ((mCollapseOffset < mCollapsibleHeight) || isDismissable())) {
+                info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+                info.addAction(AccessibilityAction.ACTION_SCROLL_UP);
+                info.setScrollable(true);
+            }
+            if (mCollapseOffset < mCollapsibleHeight) {
+                info.addAction(AccessibilityAction.ACTION_COLLAPSE);
+            }
+            if (mCollapseOffset < mCollapsibleHeight + mUncollapsibleHeight && isDismissable()) {
+                info.addAction(AccessibilityAction.ACTION_DISMISS);
+            }
         }
 
         // This view should never get accessibility focus, but it's interactive
@@ -823,12 +874,7 @@
             return true;
         }
 
-        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && mCollapseOffset != 0) {
-            smoothScrollTo(0, 0);
-            return true;
-        }
-
-        return false;
+        return performAccessibilityActionCommon(action);
     }
 
     @Override
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index a542475..fb68bc7 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -195,7 +195,6 @@
                 "android_content_res_ObbScanner.cpp",
                 "android_content_res_Configuration.cpp",
                 "android_security_Scrypt.cpp",
-                "com_android_internal_os_AtomicDirectory.cpp",
                 "com_android_internal_os_ClassLoaderFactory.cpp",
                 "com_android_internal_os_FuseAppLoop.cpp",
                 "com_android_internal_os_Zygote.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 533f403..a3fd915 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -227,7 +227,6 @@
 extern int register_android_animation_PropertyValuesHolder(JNIEnv *env);
 extern int register_android_security_Scrypt(JNIEnv *env);
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
-extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env);
 extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
 extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
@@ -1602,7 +1601,6 @@
     REG_JNI(register_android_animation_PropertyValuesHolder),
     REG_JNI(register_android_security_Scrypt),
     REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
-    REG_JNI(register_com_android_internal_os_AtomicDirectory),
     REG_JNI(register_com_android_internal_os_FuseAppLoop),
 };
 
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 78e8e13..18a1b43 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -237,10 +237,6 @@
 }
 
 void imageInfo(JNIEnv* env, jobject bitmap, AndroidBitmapInfo* info) {
-    SkASSERT(info);
-    SkASSERT(env);
-    SkASSERT(bitmap);
-    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
     jlong bitmapHandle = env->GetLongField(bitmap, gBitmap_nativePtr);
     LocalScopedBitmap localBitmap(bitmapHandle);
 
@@ -262,6 +258,9 @@
         case kAlpha_8_SkColorType:
             info->format = ANDROID_BITMAP_FORMAT_A_8;
             break;
+        case kRGBA_F16_SkColorType:
+            info->format = ANDROID_BITMAP_FORMAT_RGBA_F16;
+            break;
         default:
             info->format = ANDROID_BITMAP_FORMAT_NONE;
             break;
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 98162af..ec91cbf 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -78,14 +78,18 @@
     return nullptr;
 }
 
-static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
+        jobject source, jboolean preferAnimation) {
     if (!stream.get()) {
         return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
                                nullptr, source);
     }
     std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
     SkCodec::Result result;
-    auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
+    auto codec = SkCodec::MakeFromStream(
+            std::move(stream), &result, decoder->mPeeker.get(),
+            preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
+                            : SkCodec::SelectionPolicy::kPreferStillImage);
     if (jthrowable jexception = get_and_clear_exception(env)) {
         return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
     }
@@ -124,7 +128,7 @@
 }
 
 static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
-        jobject fileDescriptor, jobject source) {
+        jobject fileDescriptor, jboolean preferAnimation, jobject source) {
     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
 
     struct stat fdStat;
@@ -142,11 +146,11 @@
     }
 
     std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
-    return native_create(env, std::move(fileStream), source);
+    return native_create(env, std::move(fileStream), source, preferAnimation);
 }
 
 static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
-        jobject is, jbyteArray storage, jobject source) {
+        jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) {
     std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
 
     if (!stream.get()) {
@@ -157,31 +161,33 @@
     std::unique_ptr<SkStream> bufferedStream(
         SkFrontBufferedStream::Make(std::move(stream),
         SkCodec::MinBufferedBytesNeeded()));
-    return native_create(env, std::move(bufferedStream), source);
+    return native_create(env, std::move(bufferedStream), source, preferAnimation);
 }
 
-static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
-                                         jobject source) {
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/,
+        jlong assetPtr, jboolean preferAnimation, jobject source) {
     Asset* asset = reinterpret_cast<Asset*>(assetPtr);
     std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
-    return native_create(env, std::move(stream), source);
+    return native_create(env, std::move(stream), source, preferAnimation);
 }
 
-static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
-                                              jint initialPosition, jint limit, jobject source) {
+static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
+        jobject jbyteBuffer, jint initialPosition, jint limit,
+        jboolean preferAnimation, jobject source) {
     std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
                                                                      initialPosition, limit);
     if (!stream) {
         return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
                                nullptr, source);
     }
-    return native_create(env, std::move(stream), source);
+    return native_create(env, std::move(stream), source, preferAnimation);
 }
 
-static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
-                                             jint offset, jint length, jobject source) {
+static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/,
+        jbyteArray byteArray, jint offset, jint length,
+        jboolean preferAnimation, jobject source) {
     std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
-    return native_create(env, std::move(stream), source);
+    return native_create(env, std::move(stream), source, preferAnimation);
 }
 
 jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -514,11 +520,11 @@
 }
 
 static const JNINativeMethod gImageDecoderMethods[] = {
-    { "nCreate",        "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
-    { "nCreate",        "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
-    { "nCreate",        "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
-    { "nCreate",        "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
-    { "nCreate",        "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+    { "nCreate",        "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
+    { "nCreate",        "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+    { "nCreate",        "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+    { "nCreate",        "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+    { "nCreate",        "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
     { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
     { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5cbf81c..096bf69 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -497,11 +497,8 @@
 
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
 
-    std::vector<uint8_t> byteData(parcel->dataSize());
-    memcpy(byteData.data(), parcel->data(), parcel->dataSize());
-
     SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
-    transaction->setMetadata(ctrl, id, std::move(byteData));
+    transaction->setMetadata(ctrl, id, *parcel);
 }
 
 static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/jni/com_android_internal_os_AtomicDirectory.cpp b/core/jni/com_android_internal_os_AtomicDirectory.cpp
deleted file mode 100644
index 76b0fc1..0000000
--- a/core/jni/com_android_internal_os_AtomicDirectory.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <nativehelper/ScopedUtfChars.h>
-#include "jni.h"
-
-#include "core_jni_helpers.h"
-
-namespace android {
-
-static jint com_android_internal_os_AtomicDirectory_getDirectoryFd(JNIEnv* env,
-        jobject /*clazz*/, jstring path) {
-    ScopedUtfChars path8(env, path);
-    if (path8.c_str() == NULL) {
-        ALOGE("Invalid path: %s", path8.c_str());
-        return -1;
-    }
-    int fd;
-    if ((fd = TEMP_FAILURE_RETRY(open(path8.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC))) == -1) {
-        ALOGE("Cannot open directory %s, error: %s\n", path8.c_str(), strerror(errno));
-        return -1;
-    }
-    return fd;
-}
-
-static void com_android_internal_os_AtomicDirectory_fsyncDirectoryFd(JNIEnv* env,
-        jobject /*clazz*/, jint fd) {
-    if (TEMP_FAILURE_RETRY(fsync(fd)) == -1) {
-        ALOGE("Cannot fsync directory %d, error: %s\n", fd, strerror(errno));
-    }
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gRegisterMethods[] = {
-    /* name, signature, funcPtr */
-    { "fsyncDirectoryFd",
-      "(I)V",
-       (void*) com_android_internal_os_AtomicDirectory_fsyncDirectoryFd
-    },
-    { "getDirectoryFd",
-      "(Ljava/lang/String;)I",
-       (void*) com_android_internal_os_AtomicDirectory_getDirectoryFd
-    },
-};
-
-int register_com_android_internal_os_AtomicDirectory(JNIEnv* env) {
-    return RegisterMethodsOrDie(env, "com/android/internal/os/AtomicDirectory",
-            gRegisterMethods, NELEM(gRegisterMethods));
-}
-
-}; // namespace android
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 795f7ab..d5b875b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -117,7 +117,9 @@
 
 static pid_t gSystemServerPid = 0;
 
+static constexpr const char* kPropFuse = "persist.sys.fuse";
 static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
+
 static jclass gZygoteClass;
 static jmethodID gCallPostForkSystemServerHooks;
 static jmethodID gCallPostForkChildHooks;
@@ -704,15 +706,24 @@
     return;
   }
 
-  const std::string& storage_source = ExternalStorageViews[mount_mode];
-
-  BindMount(storage_source, "/storage", fail_fn);
-
-  // Mount user-specific symlink helper into place
-  userid_t user_id = multiuser_get_user_id(uid);
+  const userid_t user_id = multiuser_get_user_id(uid);
   const std::string user_source = StringPrintf("/mnt/user/%d", user_id);
+  bool isFuse = GetBoolProperty(kPropFuse, false);
+
   CreateDir(user_source, 0751, AID_ROOT, AID_ROOT, fail_fn);
-  BindMount(user_source, "/storage/self", fail_fn);
+
+  if (isFuse) {
+    // TODO(b/135341433): Bind mount the appropriate storage view for the app given its permissions
+    // media and media_location permission access. This should prevent the kernel from incorrectly
+    // sharing a cache across permission buckets
+    BindMount(user_source, "/storage", fail_fn);
+  } else {
+    const std::string& storage_source = ExternalStorageViews[mount_mode];
+    BindMount(storage_source, "/storage", fail_fn);
+
+    // Mount user-specific symlink helper into place
+    BindMount(user_source, "/storage/self", fail_fn);
+  }
 }
 
 static bool NeedsNoRandomizeWorkaround() {
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index e43b6a0..e62af74 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -20,6 +20,7 @@
 option java_multiple_files = true;
 option java_outer_classname = "SettingsServiceProto";
 
+import "frameworks/base/core/proto/android/providers/settings/config.proto";
 import "frameworks/base/core/proto/android/providers/settings/global.proto";
 import "frameworks/base/core/proto/android/providers/settings/secure.proto";
 import "frameworks/base/core/proto/android/providers/settings/system.proto";
@@ -33,6 +34,9 @@
 
     // Global settings
     optional GlobalSettingsProto global_settings = 2;
+
+    // Config settings
+    optional ConfigSettingsProto config_settings = 3;
 }
 
 message UserSettingsProto {
diff --git a/core/proto/android/providers/settings/config.proto b/core/proto/android/providers/settings/config.proto
new file mode 100644
index 0000000..cc24196
--- /dev/null
+++ b/core/proto/android/providers/settings/config.proto
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+package android.providers.settings;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/providers/settings/common.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+
+message ConfigSettingsProto {
+  option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+  repeated SettingsOperationProto historical_operations = 1;
+  repeated NamespaceProto extra_namespaces = 2;
+  repeated SettingProto activity_manager_native_boot_settings = 3;
+  repeated SettingProto activity_manager_settings = 4;
+  repeated SettingProto app_compat_settings = 5;
+  repeated SettingProto autofill_settings = 6;
+  repeated SettingProto connectivity_settings = 7;
+  repeated SettingProto content_capture_settings = 8;
+  repeated SettingProto dex_boot_settings = 9;
+  repeated SettingProto game_driver_settings = 10;
+  repeated SettingProto input_native_boot_settings = 11;
+  repeated SettingProto netd_native_settings = 12;
+  repeated SettingProto privacy_settings = 13;
+  repeated SettingProto rollback_boot_settings = 14;
+  repeated SettingProto rollback_settings = 15;
+  repeated SettingProto runtime_native_boot_settings = 16;
+  repeated SettingProto runtime_native_settings = 17;
+  repeated SettingProto runtime_settings = 18;
+  repeated SettingProto storage_settings = 19;
+  repeated SettingProto systemui_settings = 20;
+  repeated SettingProto telephony_settings = 21;
+  repeated SettingProto textclassifier_settings = 22;
+
+  message NamespaceProto {
+    optional string namespace = 1;
+    repeated SettingProto settings = 2;
+  }
+}
\ No newline at end of file
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 0070694..a2d5d2a 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -1013,7 +1013,7 @@
         optional SettingProto watchdog_poor_network_test_enabled = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto suspend_optimizations_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto verbose_logging_enabled = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        reserved 25; // connected_mac_randomization_enabled
+        reserved 25; reserved "connected_mac_randomization_enabled";
         optional SettingProto max_dhcp_retry_count = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto mobile_data_transition_wakelock_timeout_ms = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
         // Controls whether WiFi configurations created by a Device Owner app should
@@ -1025,7 +1025,7 @@
         optional SettingProto device_owner_configs_lockdown = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto frequency_band = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto p2p_device_name = 30;
-        optional SettingProto reenable_delay_ms = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        reserved 31; reserved "reenable_delay_ms";
         optional SettingProto ephemeral_out_of_range_timeout_ms = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto on_when_proxy_disconnected = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto bounce_delay_override_ms = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/stats/otaupdate/updateengine_enums.proto b/core/proto/android/stats/otaupdate/updateengine_enums.proto
new file mode 100644
index 0000000..a6e9919
--- /dev/null
+++ b/core/proto/android/stats/otaupdate/updateengine_enums.proto
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+syntax = "proto2";
+package android.stats.otaupdate;
+
+// The payload type of an OTA update attempt on A/B devices.
+enum PayloadType {
+    FULL = 10000;
+    DELTA = 10001;
+}
+
+// The attempt result reported by the update engine for an OTA update.
+enum AttemptResult {
+    UPDATE_SUCCEEDED = 10000;
+    INTERNAL_ERROR = 10001;
+    PAYLOAD_DOWNLOAD_ERROR = 10002;
+    METADATA_MALFORMED = 10003;
+    OPERATION_MALFORMED = 10004;
+    OPERATION_EXECUTION_ERROR = 10005;
+    METADATA_VERIFICATION_FAILED = 10006;
+    PAYLOAD_VERIFICATION_FAILED = 10007;
+    VERIFICATION_FAILED = 10008;
+    POSTINSTALL_FAILED = 10009;
+    ABNORMAL_TERMINATION = 10010;
+    UPDATE_CANCELED = 10011;
+    UPDATE_SUCCEEDED_NOT_ACTIVE = 10012;
+}
+
+// The error code reported by the update engine after an OTA update attempt
+// on A/B devices. More details in system/update_engine/common/error_code.h
+enum ErrorCode {
+    SUCCESS = 10000;
+    ERROR = 10001;
+    FILESYSTEM_COPIER_ERROR = 10004;
+    POST_INSTALL_RUNNER_ERROR = 10005;
+    PAYLOAD_MISMATCHED_TYPE_ERROR = 10006;
+    INSTALL_DEVICE_OPEN_ERROR = 10007;
+    KERNEL_DEVICE_OPEN_ERROR = 10008;
+    DOWNLOAD_TRANSFER_ERROR = 10009;
+    PAYLOAD_HASH_MISMATCH_ERROR = 10010;
+    PAYLOAD_SIZE_MISMATCH_ERROR = 10011;
+    DOWNLOAD_PAYLOAD_VERIFICATION_ERROR = 10012;
+    DOWNLOAD_NEW_PARTITION_INFO_ERROR = 10013;
+    DOWNLOAD_WRITE_ERROR = 10014;
+    NEW_ROOTFS_VERIFICATION_ERROR = 10015;
+    SIGNED_DELTA_PAYLOAD_EXPECTED_ERROR = 10017;
+    DOWNLOAD_PAYLOAD_PUB_KEY_VERIFICATION_ERROR = 10018;
+    DOWNLOAD_STATE_INITIALIZATION_ERROR = 10020;
+    DOWNLOAD_INVALID_METADATA_MAGIC_STRING = 10021;
+    DOWNLOAD_SIGNATURE_MISSING_IN_MANIFEST = 10022;
+    DOWNLOAD_MANIFEST_PARSE_ERROR = 10023;
+    DOWNLOAD_METADATA_SIGNATURE_ERROR = 10024;
+    DOWNLOAD_METADATA_SIGNATURE_VERIFICATION_ERROR = 10025;
+    DOWNLOAD_METADATA_SIGNATURE_MISMATCH = 10026;
+    DOWNLOAD_OPERATION_HASH_VERIFICATION_ERROR = 10027;
+    DOWNLOAD_OPERATION_EXECUTION_ERROR = 10028;
+    DOWNLOAD_OPERATION_HASH_MISMATCH = 10029;
+    DOWNLOAD_INVALID_METADATA_SIZE = 10032;
+    DOWNLOAD_INVALID_METADATA_SIGNATURE = 10033;
+    DOWNLOAD_OPERATION_HASH_MISSING_ERROR = 10038;
+    DOWNLOAD_METADATA_SIGNATURE_MISSING_ERROR = 10039;
+    UNSUPPORTED_MAJOR_PAYLOAD_VERSION = 10044;
+    UNSUPPORTED_MINOR_PAYLOAD_VERSION = 10045;
+    FILESYSTEM_VERIFIER_ERROR = 10047;
+    USER_CANCELED = 10048;
+    PAYLOAD_TIMESTAMP_ERROR = 10051;
+    UPDATED_BUT_NOT_ACTIVE = 10052;
+}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b907b97..ceccd0d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1707,6 +1707,7 @@
          manifest. -->
     <string-array name="config_forceQueryablePackages" translatable="false">
         <item>com.android.settings</item>
+        <item>com.android.providers.settings</item>
         <!-- Add packages here -->
     </string-array>
 
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 3d9a1d9..7c6271c 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -19,6 +19,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -43,6 +44,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
+import org.mockito.verification.VerificationWithTimeout;
 
 import java.util.Arrays;
 import java.util.HashSet;
@@ -56,6 +58,8 @@
 public class StartProgramListUpdatesFanoutTest {
     private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
 
+    private static final VerificationWithTimeout CB_TIMEOUT = timeout(100);
+
     // Mocks
     @Mock IBroadcastRadio mBroadcastRadioMock;
     @Mock ITunerSession mHalTunerSessionMock;
@@ -200,10 +204,10 @@
 
         // Adding mDabEnsembleInfo should not update any client.
         updateHalProgramInfo(false, Arrays.asList(mDabEnsembleInfo), null);
-        verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
-        verify(mAidlTunerCallbackMocks[1], times(2)).onProgramListUpdated(any());
-        verify(mAidlTunerCallbackMocks[2], times(1)).onProgramListUpdated(any());
-        verify(mAidlTunerCallbackMocks[3], times(2)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[1], CB_TIMEOUT.times(2)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[2], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[3], CB_TIMEOUT.times(2)).onProgramListUpdated(any());
     }
 
     @Test
@@ -240,7 +244,7 @@
 
         // Update the HAL with mModifiedAmFmInfo, and verify only the remaining client is updated.
         updateHalProgramInfo(true, Arrays.asList(mModifiedAmFmInfo), null);
-        verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
+        verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
         verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true,
                 Arrays.asList(mModifiedAmFmInfo), null);
 
@@ -313,6 +317,6 @@
         }
         ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet,
                 removedSet);
-        verify(clientMock).onProgramListUpdated(expectedChunk);
+        verify(clientMock, CB_TIMEOUT).onProgramListUpdated(expectedChunk);
     }
 }
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index bdbf368..97b7ae9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -544,7 +544,6 @@
                     Settings.Global.WIFI_ON,
                     Settings.Global.WIFI_P2P_DEVICE_NAME,
                     Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET,
-                    Settings.Global.WIFI_REENABLE_DELAY_MS,
                     Settings.Global.WIFI_RTT_BACKGROUND_EXEC_GAP_MS,
                     Settings.Global.WIFI_SAVED_STATE,
                     Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
@@ -721,7 +720,6 @@
                  Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
                  Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
                  Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
-                 Settings.Secure.FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
                  Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED);
 
     @Test
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
similarity index 83%
rename from core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
rename to core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
index 8081e9e..5f042d3 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.provider;
+package android.provider.settings.validators;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -23,7 +23,7 @@
 import static org.junit.Assert.fail;
 
 import android.platform.test.annotations.Presubmit;
-import android.provider.SettingsValidators.Validator;
+import android.provider.Settings;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -138,7 +138,7 @@
     @Test
     public void testDiscreteValueValidator() {
         String[] beerTypes = new String[]{"Ale", "American IPA", "Stout"};
-        Validator v = new SettingsValidators.DiscreteValueValidator(beerTypes);
+        Validator v = new DiscreteValueValidator(beerTypes);
         assertTrue(v.validate("Ale"));
         assertTrue(v.validate("American IPA"));
         assertTrue(v.validate("Stout"));
@@ -148,14 +148,14 @@
     @Test
     public void testDiscreteValueValidator_onNullValue_returnsFalse() {
         String[] discreteTypes = new String[]{"Type1", "Type2"};
-        Validator v = new SettingsValidators.DiscreteValueValidator(discreteTypes);
+        Validator v = new DiscreteValueValidator(discreteTypes);
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testInclusiveIntegerRangeValidator() {
-        Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+        Validator v = new InclusiveIntegerRangeValidator(0, 5);
         assertTrue(v.validate("0"));
         assertTrue(v.validate("2"));
         assertTrue(v.validate("5"));
@@ -165,14 +165,14 @@
 
     @Test
     public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+        Validator v = new InclusiveIntegerRangeValidator(0, 5);
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testInclusiveFloatRangeValidator() {
-        Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+        Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
         assertTrue(v.validate("0.0"));
         assertTrue(v.validate("2.0"));
         assertTrue(v.validate("5.0"));
@@ -182,14 +182,14 @@
 
     @Test
     public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+        Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testComponentNameListValidator() {
-        Validator v = new SettingsValidators.ComponentNameListValidator(",");
+        Validator v = new ComponentNameListValidator(",");
         assertTrue(v.validate("com.android.localtransport/.LocalTransport,"
                 + "com.google.android.gms/.backup.migrate.service.D2dTransport"));
         assertFalse(v.validate("com.google.5android,android"));
@@ -197,21 +197,21 @@
 
     @Test
     public void testComponentNameListValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.ComponentNameListValidator(",");
+        Validator v = new ComponentNameListValidator(",");
 
         assertFalse(v.validate(null));
     }
 
     @Test
     public void testPackageNameListValidator() {
-        Validator v = new SettingsValidators.PackageNameListValidator(",");
+        Validator v = new PackageNameListValidator(",");
         assertTrue(v.validate("com.android.localtransport.LocalTransport,com.google.android.gms"));
         assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android"));
     }
 
     @Test
     public void testPackageNameListValidator_onNullValue_returnsFalse() {
-        Validator v = new SettingsValidators.PackageNameListValidator(",");
+        Validator v = new PackageNameListValidator(",");
 
         assertFalse(v.validate(null));
     }
@@ -255,6 +255,45 @@
     }
 
     @Test
+    public void testTTSListValidator_withValidInput_returnsTrue() {
+        assertTrue(
+                SettingsValidators.TTS_LIST_VALIDATOR.validate(
+                        "com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
+    }
+
+    @Test
+    public void testTTSListValidator_withInvalidInput_returnsFalse() {
+        assertFalse(
+                SettingsValidators.TTS_LIST_VALIDATOR.validate(
+                        "com.foo.ttsengine:eng-USA,INVALID"));
+    }
+
+    @Test
+    public void testTTSListValidator_withEmptyInput_returnsFalse() {
+        assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(""));
+    }
+
+    @Test
+    public void testTTSListValidator_withNullInput_returnsFalse() {
+        assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null));
+    }
+
+    @Test
+    public void testTileListValidator_withValidInput_returnsTrue() {
+        assertTrue(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,2,3,4"));
+    }
+
+    @Test
+    public void testTileListValidator_withMissingValue_returnsFalse() {
+        assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,,3"));
+    }
+
+    @Test
+    public void testTileListValidator_withNullInput_returnsFalse() {
+        assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate(null));
+    }
+
+    @Test
     public void ensureAllBackedUpGlobalSettingsHaveValidators() {
         String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
                 Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 89e26da..4187c80 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -24,6 +24,7 @@
         <permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
         <permission name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+        <permission name="android.permission.INTERACT_ACROSS_USERS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.apps.tag">
@@ -86,6 +87,7 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.mtp">
+        <permission name="android.permission.ACCESS_MTP"/>
         <permission name="android.permission.MANAGE_USB"/>
     </privapp-permissions>
 
@@ -192,7 +194,6 @@
     </privapp-permissions>
 
     <privapp-permissions package="com.android.providers.media">
-        <permission name="android.permission.ACCESS_MTP"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.USE_RESERVED_DISK"/>
@@ -350,4 +351,19 @@
         <permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
     </privapp-permissions>
 
+    <privapp-permissions package="com.android.server.wifistack">
+        <permission name="android.permission.CHANGE_CONFIGURATION"/>
+        <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
+        <permission name="android.permission.DUMP"/>
+        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
+        <permission name="android.permission.MANAGE_USERS"/>
+        <permission name="android.permission.PACKAGE_USAGE_STATS"/>
+        <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
+        <permission name="android.permission.REQUEST_NETWORK_SCORES"/>
+        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+        <permission name="android.permission.UPDATE_DEVICE_STATS"/>
+        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
+        <permission name="android.permission.LOCATION_HARDWARE"/>
+    </privapp-permissions>
 </permissions>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 2d5babc..150a941 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -214,7 +214,7 @@
 
         /* @hide */
         @NonNull
-        abstract ImageDecoder createImageDecoder() throws IOException;
+        abstract ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException;
     };
 
     private static class ByteArraySource extends Source {
@@ -228,8 +228,8 @@
         private final int    mLength;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return nCreate(mData, mOffset, mLength, this);
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+            return nCreate(mData, mOffset, mLength, preferAnimation, this);
         }
     }
 
@@ -240,14 +240,14 @@
         private final ByteBuffer mBuffer;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             if (!mBuffer.isDirect() && mBuffer.hasArray()) {
                 int offset = mBuffer.arrayOffset() + mBuffer.position();
                 int length = mBuffer.limit() - mBuffer.position();
-                return nCreate(mBuffer.array(), offset, length, this);
+                return nCreate(mBuffer.array(), offset, length, preferAnimation, this);
             }
             ByteBuffer buffer = mBuffer.slice();
-            return nCreate(buffer, buffer.position(), buffer.limit(), this);
+            return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
         }
     }
 
@@ -267,7 +267,7 @@
         Resources getResources() { return mResources; }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             AssetFileDescriptor assetFd = null;
             try {
                 if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
@@ -284,26 +284,26 @@
                     throw new FileNotFoundException(mUri.toString());
                 }
 
-                return createFromStream(is, true, this);
+                return createFromStream(is, true, preferAnimation, this);
             }
-            return createFromAssetFileDescriptor(assetFd, this);
+            return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
         }
     }
 
     @NonNull
     private static ImageDecoder createFromFile(@NonNull File file,
-            @NonNull Source source) throws IOException {
+            boolean preferAnimation, @NonNull Source source) throws IOException {
         FileInputStream stream = new FileInputStream(file);
         FileDescriptor fd = stream.getFD();
         try {
             Os.lseek(fd, 0, SEEK_CUR);
         } catch (ErrnoException e) {
-            return createFromStream(stream, true, source);
+            return createFromStream(stream, true, preferAnimation, source);
         }
 
         ImageDecoder decoder = null;
         try {
-            decoder = nCreate(fd, source);
+            decoder = nCreate(fd, preferAnimation, source);
         } finally {
             if (decoder == null) {
                 IoUtils.closeQuietly(stream);
@@ -317,12 +317,12 @@
 
     @NonNull
     private static ImageDecoder createFromStream(@NonNull InputStream is,
-            boolean closeInputStream, Source source) throws IOException {
+            boolean closeInputStream, boolean preferAnimation, Source source) throws IOException {
         // Arbitrary size matches BitmapFactory.
         byte[] storage = new byte[16 * 1024];
         ImageDecoder decoder = null;
         try {
-            decoder = nCreate(is, storage, source);
+            decoder = nCreate(is, storage, preferAnimation, source);
         } finally {
             if (decoder == null) {
                 if (closeInputStream) {
@@ -340,7 +340,7 @@
 
     @NonNull
     private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
-            Source source) throws IOException {
+            boolean preferAnimation, Source source) throws IOException {
         final FileDescriptor fd = assetFd.getFileDescriptor();
         final long offset = assetFd.getStartOffset();
 
@@ -348,9 +348,9 @@
         try {
             try {
                 Os.lseek(fd, offset, SEEK_SET);
-                decoder = nCreate(fd, source);
+                decoder = nCreate(fd, preferAnimation, source);
             } catch (ErrnoException e) {
-                decoder = createFromStream(new FileInputStream(fd), true, source);
+                decoder = createFromStream(new FileInputStream(fd), true, preferAnimation, source);
             }
         } finally {
             if (decoder == null) {
@@ -388,7 +388,7 @@
         public int getDensity() { return mInputDensity; }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
 
             synchronized (this) {
                 if (mInputStream == null) {
@@ -396,7 +396,7 @@
                 }
                 InputStream is = mInputStream;
                 mInputStream = null;
-                return createFromStream(is, false, this);
+                return createFromStream(is, false, preferAnimation, this);
             }
         }
     }
@@ -434,14 +434,14 @@
         }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             synchronized (this) {
                 if (mAssetInputStream == null) {
                     throw new IOException("Cannot reuse AssetInputStreamSource");
                 }
                 AssetInputStream ais = mAssetInputStream;
                 mAssetInputStream = null;
-                return createFromAsset(ais, this);
+                return createFromAsset(ais, preferAnimation, this);
             }
         }
     }
@@ -469,7 +469,7 @@
         }
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             TypedValue value = new TypedValue();
             // This is just used in order to access the underlying Asset and
             // keep it alive.
@@ -483,7 +483,7 @@
                 }
             }
 
-            return createFromAsset((AssetInputStream) is, this);
+            return createFromAsset((AssetInputStream) is, preferAnimation, this);
         }
     }
 
@@ -491,11 +491,11 @@
      *  ImageDecoder will own the AssetInputStream.
      */
     private static ImageDecoder createFromAsset(AssetInputStream ais,
-            Source source) throws IOException {
+            boolean preferAnimation, Source source) throws IOException {
         ImageDecoder decoder = null;
         try {
             long asset = ais.getNativeAsset();
-            decoder = nCreate(asset, source);
+            decoder = nCreate(asset, preferAnimation, source);
         } finally {
             if (decoder == null) {
                 IoUtils.closeQuietly(ais);
@@ -517,9 +517,9 @@
         private final String mFileName;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             InputStream is = mAssets.open(mFileName);
-            return createFromAsset((AssetInputStream) is, this);
+            return createFromAsset((AssetInputStream) is, preferAnimation, this);
         }
     }
 
@@ -531,8 +531,8 @@
         private final File mFile;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
-            return createFromFile(mFile, this);
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+            return createFromFile(mFile, preferAnimation, this);
         }
     }
 
@@ -544,7 +544,7 @@
         private final Callable<AssetFileDescriptor> mCallable;
 
         @Override
-        public ImageDecoder createImageDecoder() throws IOException {
+        public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
             AssetFileDescriptor assetFd = null;
             try {
                 assetFd = mCallable.call();
@@ -555,7 +555,7 @@
                     throw new IOException(e);
                 }
             }
-            return createFromAssetFileDescriptor(assetFd, this);
+            return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
         }
     }
 
@@ -1740,7 +1740,7 @@
     @NonNull
     private static Drawable decodeDrawableImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
+        try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
             decoder.mSource = src;
             decoder.callHeaderDecoded(listener, src);
 
@@ -1844,7 +1844,7 @@
     @NonNull
     private static Bitmap decodeBitmapImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
-        try (ImageDecoder decoder = src.createImageDecoder()) {
+        try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
             decoder.mSource = src;
             decoder.callHeaderDecoded(listener, src);
 
@@ -1971,15 +1971,17 @@
         }
     }
 
-    private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
-    private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
-                                               int limit, Source src) throws IOException;
+    private static native ImageDecoder nCreate(long asset,
+            boolean preferAnimation, Source src) throws IOException;
+    private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
+            boolean preferAnimation, Source src) throws IOException;
     private static native ImageDecoder nCreate(byte[] data, int offset, int length,
-                                               Source src) throws IOException;
+            boolean preferAnimation, Source src) throws IOException;
     private static native ImageDecoder nCreate(InputStream is, byte[] storage,
-                                               Source src) throws IOException;
+            boolean preferAnimation, Source src) throws IOException;
     // The fd must be seekable.
-    private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
+    private static native ImageDecoder nCreate(FileDescriptor fd,
+            boolean preferAnimation, Source src) throws IOException;
     @NonNull
     private static native Bitmap nDecodeBitmap(long nativePtr,
             @NonNull ImageDecoder decoder,
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index aedb752..e713b98 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -1,8 +1,15 @@
 cc_library_host_static {
     name: "libhostgraphics",
 
+    cflags: [
+        "-Wno-unused-parameter",
+    ],
+
     srcs: [
         ":libui_host_common",
+        "Fence.cpp",
+        "HostBufferQueue.cpp",
+        "PublicFormat.cpp",
     ],
 
     include_dirs: [
diff --git a/libs/hostgraphics/Fence.cpp b/libs/hostgraphics/Fence.cpp
new file mode 100644
index 0000000..9e54816
--- /dev/null
+++ b/libs/hostgraphics/Fence.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/Fence.h>
+
+namespace android {
+
+const sp<Fence> Fence::NO_FENCE = sp<Fence>(new Fence);
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/hostgraphics/HostBufferQueue.cpp b/libs/hostgraphics/HostBufferQueue.cpp
new file mode 100644
index 0000000..ec30437
--- /dev/null
+++ b/libs/hostgraphics/HostBufferQueue.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferQueue.h>
+
+namespace android {
+
+class HostBufferQueue : public IGraphicBufferProducer, public IGraphicBufferConsumer {
+public:
+    HostBufferQueue() : mWidth(0), mHeight(0) { }
+
+    virtual status_t setConsumerIsProtected(bool isProtected) { return OK; }
+
+    virtual status_t detachBuffer(int slot) { return OK; }
+
+    virtual status_t getReleasedBuffers(uint64_t* slotMask) { return OK; }
+
+    virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) {
+        mWidth = w;
+        mHeight = h;
+        mBuffer = sp<GraphicBuffer>(new GraphicBuffer(mWidth, mHeight));
+        return OK;
+    }
+
+    virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) { return OK; }
+
+    virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) { return OK; }
+
+    virtual status_t discardFreeBuffers() { return OK; }
+
+    virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
+                                       uint64_t maxFrameNumber = 0) {
+        buffer->mGraphicBuffer = mBuffer;
+        buffer->mSlot = 0;
+        return OK;
+    }
+
+    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) { return OK; }
+
+    virtual status_t setConsumerUsageBits(uint64_t usage) { return OK; }
+private:
+    sp<GraphicBuffer> mBuffer;
+    uint32_t mWidth;
+    uint32_t mHeight;
+};
+
+void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+        sp<IGraphicBufferConsumer>* outConsumer) {
+
+    sp<HostBufferQueue> obj(new HostBufferQueue());
+
+    *outProducer = obj;
+    *outConsumer = obj;
+}
+
+} // namespace android
diff --git a/libs/hostgraphics/PublicFormat.cpp b/libs/hostgraphics/PublicFormat.cpp
new file mode 100644
index 0000000..af6d273
--- /dev/null
+++ b/libs/hostgraphics/PublicFormat.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ui/PublicFormat.h>
+
+namespace android {
+
+android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) {
+    return static_cast<android_dataspace>(0);
+}
+
+int mapPublicFormatToHalFormat(PublicFormat f) {
+    return static_cast<int>(f);
+}
+
+PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) {
+    return static_cast<PublicFormat>(format);
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/gui/BufferItem.h
new file mode 100644
index 0000000..01409e1
--- /dev/null
+++ b/libs/hostgraphics/gui/BufferItem.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_GUI_BUFFERITEM_H
+#define ANDROID_GUI_BUFFERITEM_H
+
+#include <ui/Fence.h>
+#include <ui/Rect.h>
+
+#include <system/graphics.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+// The only thing we need here for layoutlib is mGraphicBuffer. The rest of the fields are added
+// just to satisfy the calls from the android_media_ImageReader.h
+
+class BufferItem {
+public:
+    enum { INVALID_BUFFER_SLOT = -1 };
+
+    BufferItem() : mGraphicBuffer(nullptr), mFence(Fence::NO_FENCE) {}
+    ~BufferItem() {}
+
+    sp<GraphicBuffer> mGraphicBuffer;
+
+    sp<Fence> mFence;
+
+    Rect mCrop;
+
+    uint32_t mTransform;
+
+    uint32_t mScalingMode;
+
+    int64_t mTimestamp;
+
+    android_dataspace mDataSpace;
+
+    uint64_t mFrameNumber;
+
+    int mSlot;
+
+    bool mTransformToDisplayInverse;
+};
+
+}
+
+#endif // ANDROID_GUI_BUFFERITEM_H
diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/gui/BufferItemConsumer.h
new file mode 100644
index 0000000..707b313
--- /dev/null
+++ b/libs/hostgraphics/gui/BufferItemConsumer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_GUI_BUFFERITEMCONSUMER_H
+#define ANDROID_GUI_BUFFERITEMCONSUMER_H
+
+#include <utils/RefBase.h>
+
+#include <gui/ConsumerBase.h>
+#include <gui/IGraphicBufferConsumer.h>
+
+namespace android {
+
+class BufferItemConsumer : public ConsumerBase {
+public:
+    BufferItemConsumer(
+        const sp<IGraphicBufferConsumer>& consumer,
+        uint64_t consumerUsage,
+        int bufferCount,
+        bool controlledByApp) : mConsumer(consumer) {
+    }
+
+    status_t acquireBuffer(BufferItem *item, nsecs_t presentWhen, bool waitForFence = true) {
+        return mConsumer->acquireBuffer(item, presentWhen, 0);
+    }
+
+    status_t releaseBuffer(
+        const BufferItem &item, const sp<Fence>& releaseFence = Fence::NO_FENCE) { return OK; }
+
+    void setName(const String8& name) { }
+
+    void setFrameAvailableListener(const wp<FrameAvailableListener>& listener) { }
+
+    status_t setDefaultBufferSize(uint32_t width, uint32_t height) {
+        return mConsumer->setDefaultBufferSize(width, height);
+    }
+
+    status_t setDefaultBufferFormat(PixelFormat defaultFormat) {
+        return mConsumer->setDefaultBufferFormat(defaultFormat);
+    }
+
+    status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) {
+        return mConsumer->setDefaultBufferDataSpace(defaultDataSpace);
+    }
+
+    void abandon() { }
+
+    status_t detachBuffer(int slot) { return OK; }
+
+    status_t discardFreeBuffers() { return OK; }
+
+    void freeBufferLocked(int slotIndex) { }
+
+    status_t addReleaseFenceLocked(
+        int slot, const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) { return OK; }
+private:
+    sp<IGraphicBufferConsumer> mConsumer;
+};
+
+} // namespace android
+
+#endif // ANDROID_GUI_BUFFERITEMCONSUMER_H
diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/gui/BufferQueue.h
new file mode 100644
index 0000000..aa3e726
--- /dev/null
+++ b/libs/hostgraphics/gui/BufferQueue.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_GUI_BUFFERQUEUE_H
+#define ANDROID_GUI_BUFFERQUEUE_H
+
+#include <gui/BufferItem.h>
+#include <gui/IGraphicBufferConsumer.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+class BufferQueue {
+public:
+    enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT };
+    enum { NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE };
+
+    static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
+            sp<IGraphicBufferConsumer>* outConsumer);
+};
+
+} // namespace android
+
+#endif // ANDROID_GUI_BUFFERQUEUE_H
diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/gui/ConsumerBase.h
new file mode 100644
index 0000000..9002953
--- /dev/null
+++ b/libs/hostgraphics/gui/ConsumerBase.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_GUI_CONSUMERBASE_H
+#define ANDROID_GUI_CONSUMERBASE_H
+
+#include <gui/BufferItem.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ConsumerBase : public virtual RefBase {
+public:
+    struct FrameAvailableListener : public virtual RefBase {
+        // See IConsumerListener::onFrame{Available,Replaced}
+        virtual void onFrameAvailable(const BufferItem& item) = 0;
+        virtual void onFrameReplaced(const BufferItem& /* item */) {}
+    };
+};
+
+} // namespace android
+
+#endif // ANDROID_GUI_CONSUMERBASE_H
\ No newline at end of file
diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/gui/IGraphicBufferConsumer.h
new file mode 100644
index 0000000..9eb67b2
--- /dev/null
+++ b/libs/hostgraphics/gui/IGraphicBufferConsumer.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include <ui/PixelFormat.h>
+
+#include <utils/Errors.h>
+
+namespace android {
+
+class BufferItem;
+class Fence;
+class GraphicBuffer;
+
+class IGraphicBufferConsumer : virtual public RefBase {
+public:
+    enum {
+        // Returned by releaseBuffer, after which the consumer must free any references to the
+        // just-released buffer that it might have.
+        STALE_BUFFER_SLOT = 1,
+        // Returned by dequeueBuffer if there are no pending buffers available.
+        NO_BUFFER_AVAILABLE,
+        // Returned by dequeueBuffer if it's too early for the buffer to be acquired.
+        PRESENT_LATER,
+    };
+
+    virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen,
+                                   uint64_t maxFrameNumber = 0) = 0;
+
+    virtual status_t detachBuffer(int slot) = 0;
+
+    virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
+
+    virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
+
+    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
+
+    virtual status_t setDefaultBufferFormat(PixelFormat defaultFormat) = 0;
+
+    virtual status_t setDefaultBufferDataSpace(android_dataspace defaultDataSpace) = 0;
+
+    virtual status_t setConsumerUsageBits(uint64_t usage) = 0;
+
+    virtual status_t setConsumerIsProtected(bool isProtected) = 0;
+
+    virtual status_t discardFreeBuffers() = 0;
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/gui/IGraphicBufferProducer.h
index 0042213..a1efd0b 100644
--- a/libs/hostgraphics/gui/IGraphicBufferProducer.h
+++ b/libs/hostgraphics/gui/IGraphicBufferProducer.h
@@ -19,6 +19,8 @@
 
 #include <utils/RefBase.h>
 
+#include <ui/GraphicBuffer.h>
+
 namespace android {
 
 class IGraphicBufferProducer : virtual public RefBase {
diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/ui/Fence.h
new file mode 100644
index 0000000..04d535c
--- /dev/null
+++ b/libs/hostgraphics/ui/Fence.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_FENCE_H
+#define ANDROID_FENCE_H
+
+#include <utils/String8.h>
+#include <utils/RefBase.h>
+
+typedef int64_t nsecs_t;
+
+namespace android {
+
+class Fence : public LightRefBase<Fence> {
+public:
+    Fence() { }
+    Fence(int) { }
+    static const sp<Fence> NO_FENCE;
+    static constexpr nsecs_t SIGNAL_TIME_PENDING = INT64_MAX;
+    static constexpr nsecs_t SIGNAL_TIME_INVALID = -1;
+    static sp<Fence> merge(const char* name, const sp<Fence>& f1, const sp<Fence>& f2) {
+        return NO_FENCE;
+    }
+
+    static sp<Fence> merge(const String8& name, const sp<Fence>& f1, const sp<Fence>& f2) {
+        return NO_FENCE;
+    }
+
+    enum class Status {
+        Invalid,     // Fence is invalid
+        Unsignaled,  // Fence is valid but has not yet signaled
+        Signaled,    // Fence is valid and has signaled
+    };
+
+    status_t wait(int timeout) { return OK; }
+
+    status_t waitForever(const char* logname) { return OK; }
+
+    int dup() const { return 0; }
+
+    inline Status getStatus() {
+        // The sync_wait call underlying wait() has been measured to be
+        // significantly faster than the sync_fence_info call underlying
+        // getSignalTime(), which might otherwise appear to be the more obvious
+        // way to check whether a fence has signaled.
+        switch (wait(0)) {
+            case NO_ERROR:
+                return Status::Signaled;
+            case -ETIME:
+                return Status::Unsignaled;
+            default:
+                return Status::Invalid;
+        }
+    }
+};
+
+} // namespace android
+
+#endif // ANDROID_FENCE_H
diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/ui/GraphicBuffer.h
new file mode 100644
index 0000000..ac88e44
--- /dev/null
+++ b/libs/hostgraphics/ui/GraphicBuffer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef ANDROID_GRAPHIC_BUFFER_H
+#define ANDROID_GRAPHIC_BUFFER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <vector>
+
+#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class GraphicBuffer : virtual public RefBase {
+public:
+    GraphicBuffer(uint32_t w, uint32_t h):width(w),height(h) {
+        data.resize(w*h);
+    }
+    uint32_t getWidth() const           { return static_cast<uint32_t>(width); }
+    uint32_t getHeight() const          { return static_cast<uint32_t>(height); }
+    uint32_t getStride() const          { return static_cast<uint32_t>(width); }
+    uint64_t getUsage() const           { return 0; }
+    PixelFormat getPixelFormat() const  { return PIXEL_FORMAT_RGBA_8888; }
+    //uint32_t getLayerCount() const      { return static_cast<uint32_t>(layerCount); }
+    Rect getBounds() const              { return Rect(width, height); }
+
+    status_t lockAsyncYCbCr(uint32_t inUsage, const Rect& rect,
+            android_ycbcr *ycbcr, int fenceFd) { return OK; }
+
+    status_t lockAsync(uint32_t inUsage, const Rect& rect, void** vaddr, int fenceFd,
+                       int32_t* outBytesPerPixel = nullptr, int32_t* outBytesPerStride = nullptr) {
+        *vaddr = data.data();
+        return OK;
+    }
+
+    status_t unlockAsync(int *fenceFd) { return OK; }
+
+private:
+    uint32_t width;
+    uint32_t height;
+    std::vector<uint32_t> data;
+};
+
+}; // namespace android
+
+#endif // ANDROID_GRAPHIC_BUFFER_H
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 5100165..35abc57 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -138,14 +138,14 @@
     err = mCreateInstance(&instance_create, nullptr, &mInstance);
     LOG_ALWAYS_FATAL_IF(err < 0);
 
+    GET_INST_PROC(CreateDevice);
     GET_INST_PROC(DestroyInstance);
+    GET_INST_PROC(EnumerateDeviceExtensionProperties);
     GET_INST_PROC(EnumeratePhysicalDevices);
-    GET_INST_PROC(GetPhysicalDeviceProperties);
-    GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
     GET_INST_PROC(GetPhysicalDeviceFeatures2);
     GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2);
-    GET_INST_PROC(CreateDevice);
-    GET_INST_PROC(EnumerateDeviceExtensionProperties);
+    GET_INST_PROC(GetPhysicalDeviceProperties);
+    GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
 
     uint32_t gpuCount;
     LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr));
@@ -312,29 +312,27 @@
 
     LOG_ALWAYS_FATAL_IF(mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice));
 
-    GET_DEV_PROC(GetDeviceQueue);
-    GET_DEV_PROC(DeviceWaitIdle);
-    GET_DEV_PROC(DestroyDevice);
-    GET_DEV_PROC(CreateCommandPool);
-    GET_DEV_PROC(DestroyCommandPool);
     GET_DEV_PROC(AllocateCommandBuffers);
-    GET_DEV_PROC(FreeCommandBuffers);
-    GET_DEV_PROC(ResetCommandBuffer);
     GET_DEV_PROC(BeginCommandBuffer);
-    GET_DEV_PROC(EndCommandBuffer);
     GET_DEV_PROC(CmdPipelineBarrier);
+    GET_DEV_PROC(CreateCommandPool);
+    GET_DEV_PROC(CreateFence);
+    GET_DEV_PROC(CreateSemaphore);
+    GET_DEV_PROC(DestroyCommandPool);
+    GET_DEV_PROC(DestroyDevice);
+    GET_DEV_PROC(DestroyFence);
+    GET_DEV_PROC(DestroySemaphore);
+    GET_DEV_PROC(DeviceWaitIdle);
+    GET_DEV_PROC(EndCommandBuffer);
+    GET_DEV_PROC(FreeCommandBuffers);
     GET_DEV_PROC(GetDeviceQueue);
+    GET_DEV_PROC(GetSemaphoreFdKHR);
+    GET_DEV_PROC(ImportSemaphoreFdKHR);
     GET_DEV_PROC(QueueSubmit);
     GET_DEV_PROC(QueueWaitIdle);
-    GET_DEV_PROC(DeviceWaitIdle);
-    GET_DEV_PROC(CreateSemaphore);
-    GET_DEV_PROC(DestroySemaphore);
-    GET_DEV_PROC(ImportSemaphoreFdKHR);
-    GET_DEV_PROC(GetSemaphoreFdKHR);
-    GET_DEV_PROC(CreateFence);
-    GET_DEV_PROC(DestroyFence);
-    GET_DEV_PROC(WaitForFences);
+    GET_DEV_PROC(ResetCommandBuffer);
     GET_DEV_PROC(ResetFences);
+    GET_DEV_PROC(WaitForFences);
 }
 
 void VulkanManager::initialize() {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 89d3cc4..16f2917 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,6 +20,7 @@
     ],
 
     shared_libs: [
+        "libbinder",
         "libcutils",
         "liblog",
         "libutils",
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..fd386e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -245,7 +245,8 @@
         if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
                 || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
                         | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
-                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+                        | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
+                        | DIRTY_ICON_STYLE))))) {
             needApplyTransaction = true;
 
             if (wantSurfaceVisibleAndDrawn
@@ -274,6 +275,21 @@
                         update.state.transformationMatrix.dtdy);
             }
 
+            if (wantSurfaceVisibleAndDrawn
+                    && (becomingVisible
+                            || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+                Parcel p;
+                p.writeInt32(update.state.icon.style);
+                p.writeFloat(update.state.icon.hotSpotX);
+                p.writeFloat(update.state.icon.hotSpotY);
+
+                // Pass cursor metadata in the sprite surface so that when Android is running as a
+                // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
+                // update mouse cursor in the host OS.
+                t.setMetadata(
+                        update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+            }
+
             int32_t surfaceLayer = mOverlayLayer + update.state.layer;
             if (wantSurfaceVisibleAndDrawn
                     && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +413,14 @@
         } else {
             dirty = DIRTY_BITMAP;
         }
+
+        if (mLocked.state.icon.style != icon.style) {
+            mLocked.state.icon.style = icon.style;
+            dirty |= DIRTY_ICON_STYLE;
+        }
     } else if (mLocked.state.icon.isValid()) {
         mLocked.state.icon.bitmap.reset();
-        dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+        dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
     } else {
         return; // setting to invalid icon and already invalid so nothing to do
     }
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..79a904f 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -55,11 +55,12 @@
  * Icon that a sprite displays, including its hotspot.
  */
 struct SpriteIcon {
-    inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
-    inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
-            bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+    inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
+    inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+            bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
 
     SkBitmap bitmap;
+    int32_t style;
     float hotSpotX;
     float hotSpotY;
 
@@ -69,11 +70,12 @@
             bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
                     0, 0);
         }
-        return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
+        return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
     }
 
     inline void reset() {
         bitmap.reset();
+        style = 0;
         hotSpotX = 0;
         hotSpotY = 0;
     }
@@ -149,15 +151,15 @@
     SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
 
     /* Creates a new sprite, initially invisible. */
-    sp<Sprite> createSprite();
+    virtual sp<Sprite> createSprite();
 
     /* Opens or closes a transaction to perform a batch of sprite updates as part of
      * a single operation such as setPosition and setAlpha.  It is not necessary to
      * open a transaction when updating a single property.
      * Calls to openTransaction() nest and must be matched by an equal number
      * of calls to closeTransaction(). */
-    void openTransaction();
-    void closeTransaction();
+    virtual void openTransaction();
+    virtual void closeTransaction();
 
 private:
     enum {
@@ -174,6 +176,7 @@
         DIRTY_VISIBILITY = 1 << 5,
         DIRTY_HOTSPOT = 1 << 6,
         DIRTY_DISPLAY_ID = 1 << 7,
+        DIRTY_ICON_STYLE = 1 << 8,
     };
 
     /* Describes the state of a sprite.
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..fe74c62
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "presubmit": [
+        {
+            "name": "libinputservice_test"
+        }
+    ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..e83b2a7
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2019 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.
+
+cc_test {
+    name: "libinputservice_test",
+    srcs: [
+        "PointerController_test.cpp",
+    ],
+    shared_libs: [
+        "libinputservice",
+        "libgui",
+        "libhwui",
+        "libutils",
+    ],
+    static_libs: [
+        "libgmock",
+        "libgtest",
+    ],
+    header_libs: [
+        "libbase_headers",
+        "libinputflinger_headers",
+    ],
+    include_dirs: [
+        "frameworks/base/libs",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
new file mode 100644
index 0000000..92efb4e
--- /dev/null
+++ b/libs/input/tests/PointerController_test.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+
+#include <atomic>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+enum TestCursorType {
+    CURSOR_TYPE_DEFAULT = 0,
+    CURSOR_TYPE_HOVER,
+    CURSOR_TYPE_TOUCH,
+    CURSOR_TYPE_ANCHOR,
+    CURSOR_TYPE_ADDITIONAL_1,
+    CURSOR_TYPE_ADDITIONAL_2,
+    CURSOR_TYPE_CUSTOM = -1,
+};
+
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::NiceMock;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::Test;
+
+std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
+    return std::make_pair(type * 10, type * 10 + 5);
+}
+
+class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
+public:
+    virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
+    virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
+    virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+            std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
+    virtual int32_t getDefaultPointerIconId() override;
+    virtual int32_t getCustomPointerIconId() override;
+
+private:
+    void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+};
+
+void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
+    loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+}
+
+void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
+        int32_t) {
+    loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
+    loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
+    loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+}
+
+void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
+        std::map<int32_t, SpriteIcon>* outResources,
+        std::map<int32_t, PointerAnimation>* outAnimationResources,
+        int32_t) {
+    SpriteIcon icon;
+    PointerAnimation anim;
+
+    for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) {
+        loadPointerIconForType(&icon, cursorType);
+        anim.animationFrames.push_back(icon);
+        anim.durationPerFrame = 10;
+        (*outResources)[cursorType] = icon;
+        (*outAnimationResources)[cursorType] = anim;
+    }
+}
+
+int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+    return CURSOR_TYPE_DEFAULT;
+}
+
+int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+    return CURSOR_TYPE_CUSTOM;
+}
+
+void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
+    icon->style = type;
+    std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
+    icon->hotSpotX = hotSpot.first;
+    icon->hotSpotY = hotSpot.second;
+}
+
+class PointerControllerTest : public Test {
+protected:
+    PointerControllerTest();
+    ~PointerControllerTest();
+
+    sp<MockSprite> mPointerSprite;
+    sp<MockPointerControllerPolicyInterface> mPolicy;
+    sp<MockSpriteController> mSpriteController;
+    sp<PointerController> mPointerController;
+
+private:
+    void loopThread();
+
+    std::atomic<bool> mRunning = true;
+    class MyLooper : public Looper {
+    public:
+        MyLooper() : Looper(false) {}
+        ~MyLooper() = default;
+    };
+    sp<MyLooper> mLooper;
+    std::thread mThread;
+};
+
+PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
+        mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+
+    mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+    mPolicy = new MockPointerControllerPolicyInterface();
+
+    EXPECT_CALL(*mSpriteController, createSprite())
+            .WillOnce(Return(mPointerSprite));
+
+    mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+
+    DisplayViewport viewport;
+    viewport.displayId = ADISPLAY_ID_DEFAULT;
+    viewport.logicalRight = 1600;
+    viewport.logicalBottom = 1200;
+    viewport.physicalRight = 800;
+    viewport.physicalBottom = 600;
+    viewport.deviceWidth = 400;
+    viewport.deviceHeight = 300;
+    mPointerController->setDisplayViewport(viewport);
+}
+
+PointerControllerTest::~PointerControllerTest() {
+    mRunning.store(false, std::memory_order_relaxed);
+    mThread.join();
+}
+
+void PointerControllerTest::loopThread() {
+    Looper::setForThread(mLooper);
+
+    while (mRunning.load(std::memory_order_relaxed)) {
+        mLooper->pollOnce(100);
+    }
+}
+
+TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
+                    Field(&SpriteIcon::hotSpotX, hotspot.first),
+                    Field(&SpriteIcon::hotSpotY, hotspot.second))));
+    mPointerController->reloadPointerResources();
+}
+
+TEST_F(PointerControllerTest, updatePointerIcon) {
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    int32_t type = CURSOR_TYPE_ADDITIONAL_1;
+    std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, type),
+                    Field(&SpriteIcon::hotSpotX, hotspot.first),
+                    Field(&SpriteIcon::hotSpotY, hotspot.second))));
+    mPointerController->updatePointerIcon(type);
+}
+
+TEST_F(PointerControllerTest, setCustomPointerIcon) {
+    mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+    int32_t style = CURSOR_TYPE_CUSTOM;
+    float hotSpotX = 15;
+    float hotSpotY = 20;
+
+    SpriteIcon icon;
+    icon.style = style;
+    icon.hotSpotX = hotSpotX;
+    icon.hotSpotY = hotSpotY;
+
+    EXPECT_CALL(*mPointerSprite, setVisible(true));
+    EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+    EXPECT_CALL(*mPointerSprite, setIcon(
+            AllOf(
+                    Field(&SpriteIcon::style, style),
+                    Field(&SpriteIcon::hotSpotX, hotSpotX),
+                    Field(&SpriteIcon::hotSpotY, hotSpotY))));
+    mPointerController->setCustomPointerIcon(icon);
+}
+
+}  // namespace android
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
new file mode 100644
index 0000000..013b79c
--- /dev/null
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef _MOCK_SPRITE_H
+#define _MOCK_SPRITE_H
+
+#include <input/SpriteController.h>
+
+#include <gmock/gmock.h>
+
+namespace android {
+
+class MockSprite : public Sprite {
+public:
+    virtual ~MockSprite() = default;
+
+    MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
+    MOCK_METHOD(void, setVisible, (bool), (override));
+    MOCK_METHOD(void, setPosition, (float, float), (override));
+    MOCK_METHOD(void, setLayer, (int32_t), (override));
+    MOCK_METHOD(void, setAlpha, (float), (override));
+    MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
+    MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+};
+
+}  // namespace android
+
+#endif  // _MOCK_SPRITE_H
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
new file mode 100644
index 0000000..a034f66
--- /dev/null
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef _MOCK_SPRITE_CONTROLLER_H
+#define _MOCK_SPRITE_CONTROLLER_H
+
+#include "MockSprite.h"
+
+#include <input/SpriteController.h>
+
+namespace android {
+
+class MockSpriteController : public SpriteController {
+
+public:
+    MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+    ~MockSpriteController() {}
+
+    MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
+    MOCK_METHOD(void, openTransaction, (), (override));
+    MOCK_METHOD(void, closeTransaction, (), (override));
+};
+
+}  // namespace android
+
+#endif  // _MOCK_SPRITE_CONTROLLER_H
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index ab01ddb..db63889 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -22,6 +22,8 @@
     ],
     api_packages: ["com.android.location.provider"],
     srcs_lib: "framework-minus-apex",
-    srcs_lib_whitelist_dirs: ["location/java"],
-    srcs_lib_whitelist_pkgs: ["com.android.internal.location"],
+    // TODO(b/70046217): remove core/java and android below. It was added to provide definitions for
+    // types like android.os.Bundle
+    srcs_lib_whitelist_dirs: ["core/java", "location/java"],
+    srcs_lib_whitelist_pkgs: ["android", "com.android.internal.location"],
 }
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 148ffaf..55583d5 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -37,6 +37,7 @@
 import libcore.io.Streams;
 
 import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
 import java.io.DataInput;
 import java.io.DataInputStream;
@@ -1866,14 +1867,17 @@
 
         FileInputStream in = null;
         FileOutputStream out = null;
+        File originalFile = null;
+        if (mFilename != null) {
+            originalFile = new File(mFilename);
+        }
         File tempFile = null;
         try {
             // Move the original file to temporary file.
             if (mFilename != null) {
                 tempFile = new File(mFilename + ".tmp");
-                File originalFile = new File(mFilename);
                 if (!originalFile.renameTo(tempFile)) {
-                    throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
+                    throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
                 }
             } else if (mSeekableFileDescriptor != null) {
                 tempFile = File.createTempFile("temp", "jpg");
@@ -1882,8 +1886,8 @@
                 out = new FileOutputStream(tempFile);
                 Streams.copy(in, out);
             }
-        } catch (ErrnoException e) {
-            throw e.rethrowAsIOException();
+        } catch (Exception e) {
+            throw new IOException("Failed to copy original file to temp file", e);
         } finally {
             IoUtils.closeQuietly(in);
             IoUtils.closeQuietly(out);
@@ -1900,9 +1904,18 @@
                 Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
                 out = new FileOutputStream(mSeekableFileDescriptor);
             }
-            saveJpegAttributes(in, out);
-        } catch (ErrnoException e) {
-            throw e.rethrowAsIOException();
+            try (BufferedInputStream bufferedIn = new BufferedInputStream(in);
+                 BufferedOutputStream bufferedOut = new BufferedOutputStream(out)) {
+                saveJpegAttributes(bufferedIn, bufferedOut);
+            }
+        } catch (Exception e) {
+            if (mFilename != null) {
+                if (!tempFile.renameTo(originalFile)) {
+                    throw new IOException("Couldn't restore original file: "
+                            + originalFile.getAbsolutePath());
+                }
+            }
+            throw new IOException("Failed to save new file", e);
         } finally {
             IoUtils.closeQuietly(in);
             IoUtils.closeQuietly(out);
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index 792a2ba..be0d966 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -272,6 +272,15 @@
     @UnsupportedAppUsage
     public Metadata() { }
 
+    // Have to declare protected for finalize() since it is protected
+    // in the base class Object.
+    @Override
+    protected void finalize() throws Throwable {
+        if (mParcel != null) {
+            mParcel.recycle();
+        }
+    }
+
     /**
      * Go over all the records, collecting metadata keys and records'
      * type field offset in the Parcel. These are stored in
@@ -418,6 +427,10 @@
             parcel.setDataPosition(pin);
             return false;
         }
+
+        if (mParcel != null) {
+            mParcel.recycle();
+        }
         mParcel = parcel;
         return true;
     }
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index 195d4fe..261b9f5 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -21,4 +21,8 @@
         coreApp="true">
     <!-- This permission is required to monitor car power state. -->
     <uses-permission android:name="android.car.permission.CAR_POWER" />
+    <!-- This permission is required to get the trusted device list of a user. -->
+    <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/>
+    <!-- This permission is required to get bluetooth broadcast. -->
+    <uses-permission android:name="android.permission.BLUETOOTH" />
 </manifest>
diff --git a/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml b/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml
new file mode 100644
index 0000000..bec6ba7
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+  <solid android:color="@color/unlock_dialog_background_color"/>
+  <padding
+      android:bottom="@*android:dimen/car_padding_2"
+      android:left="@*android:dimen/car_padding_2"
+      android:right="@*android:dimen/car_padding_2"
+      android:top="@*android:dimen/car_padding_2"/>
+  <corners
+      android:radius="@dimen/unlock_dialog_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
new file mode 100644
index 0000000..2d9901c
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 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
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center">
+
+    <LinearLayout
+        android:layout_width="@dimen/unlock_dialog_width"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_gravity="center"
+        android:orientation="vertical"
+        android:background="@drawable/unlock_dialog_background"
+        android:padding="@*android:dimen/car_padding_2">
+        <FrameLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+            <ProgressBar
+                android:layout_gravity="center"
+                android:layout_width="@dimen/unlock_dialog_progress_bar_size"
+                android:layout_height="@dimen/unlock_dialog_progress_bar_size" />
+            <ImageView
+                android:id="@+id/avatar"
+                android:layout_gravity="center"
+                android:layout_width="@dimen/unlock_dialog_avatar_size"
+                android:layout_height="@dimen/unlock_dialog_avatar_size"
+                android:scaleType="fitCenter"/>
+        </FrameLayout>
+
+        <TextView
+            android:id="@+id/user_name"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/unlock_dialog_default_user_name"
+            android:textSize="@*android:dimen/car_body1_size"
+            android:textColor="@android:color/white"/>
+
+        <TextView
+            android:id="@+id/unlocking_text"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_marginTop="@*android:dimen/car_padding_1"
+            android:text="@string/unlock_dialog_message_default"
+            android:textSize="@*android:dimen/car_body4_size"
+            android:textColor="@color/unlock_dialog_message_text_default"/>
+
+        <Button
+            android:id="@+id/enter_pin_button"
+            android:layout_marginTop="@*android:dimen/car_padding_1"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:text="@string/unlock_dialog_button_text_pin"
+            style="@style/UnlockDialogButton"/>
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml
index 69ab3f3..0a3f7aa 100644
--- a/packages/CarSystemUI/res/values/colors_car.xml
+++ b/packages/CarSystemUI/res/values/colors_car.xml
@@ -26,4 +26,9 @@
     <color name="car_user_switcher_add_user_background_color">@*android:color/car_dark_blue_grey_600</color>
     <color name="car_user_switcher_add_user_add_sign_color">@*android:color/car_body1_light</color>
 
+    <!-- colors for unlock dialog -->
+    <color name="unlock_dialog_background_color">#ff282a2d</color>
+    <color name="unlock_dialog_message_text_default">@*android:color/car_grey_400</color>
+    <color name="unlock_dialog_enter_pin_text_color">#ff66b5ff</color>
+
 </resources>
diff --git a/packages/CarSystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml
index 42a7649..9cb09c9 100644
--- a/packages/CarSystemUI/res/values/dimens_car.xml
+++ b/packages/CarSystemUI/res/values/dimens_car.xml
@@ -43,4 +43,10 @@
     <!-- This must be the negative of car_user_switcher_container_height for the animation. -->
     <dimen name="car_user_switcher_container_anim_height">-420dp</dimen>
 
+    <!-- dimensions for the unlock dialog -->
+    <dimen name="unlock_dialog_width">500dp</dimen>
+    <dimen name="unlock_dialog_radius">16dp</dimen>
+    <dimen name="unlock_dialog_avatar_size">100dp</dimen>
+    <dimen name="unlock_dialog_progress_bar_size">140dp</dimen>
+
 </resources>
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index be2cb0d..862ba75 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -31,5 +31,7 @@
     <!--Percentage of the screen height, from the bottom, that a notification panel being peeked
     at will result in remaining closed the panel if released-->
     <integer name="notification_settle_close_percentage">80</integer>
+    <!-- The delay before the unlock dialog pops up -->
+    <integer name="unlock_dialog_delay_ms">3000</integer>
 
 </resources>
diff --git a/packages/CarSystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml
index 83e91c5..717692e 100644
--- a/packages/CarSystemUI/res/values/strings_car.xml
+++ b/packages/CarSystemUI/res/values/strings_car.xml
@@ -29,4 +29,19 @@
     <string name="user_add_user_message_setup">When you add a new user, that person needs to set up their space.</string>
     <!-- Message to inform user that the newly created user will have permissions to update apps for all other users. [CHAR LIMIT=100] -->
     <string name="user_add_user_message_update">Any user can update apps for all other users.</string>
+    <!-- Default messages displayed on the unlock dialog before unlock advertising started. [CHAR LIMIT=30]-->
+    <string name="unlock_dialog_message_default">Waiting\u2026</string>
+    <!-- Message to inform user that the IHU is looking for trusted device. [CHAR LIMIT=30] -->
+    <string name="unlock_dialog_message_start">Looking for trusted device\u2026</string>
+
+    <!-- Cancel Button text for user who has PIN as security lock. [CHAR LIMIT=30] -->
+    <string name="unlock_dialog_button_text_pin">Enter PIN instead</string>
+    <!-- Cancel Button text for user who has Pattern as security lock. [CHAR LIMIT=30] -->
+    <string name="unlock_dialog_button_text_pattern">Enter Pattern instead</string>
+    <!-- Cancel Button text for user who has Password as security lock. [CHAR LIMIT=30] -->
+    <string name="unlock_dialog_button_text_password">Enter Password instead</string>
+    <!-- Default user name shows on unlock dialog -->
+    <string name="unlock_dialog_default_user_name">Default Name</string>
+    <!-- Default title for unlock dialog -->
+    <string name="unlock_dialog_title">Unlock Dialogue</string>
 </resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 371bebd..a9423bf 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -46,4 +46,12 @@
         <item name="android:layout_width">96dp</item>
         <item name="android:background">@drawable/nav_button_background</item>
     </style>
+
+    <style name="UnlockDialogButton">
+        <item name="android:background">?android:attr/selectableItemBackground</item>
+        <item name="android:textAlignment">center</item>
+        <item name="android:textColor">@color/unlock_dialog_enter_pin_text_color</item>
+        <item name="android:paddingHorizontal">16dp</item>
+        <item name="android:textAllCaps">false</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 37b5e88..e95103b 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -26,6 +26,7 @@
 import android.car.drivingstate.CarDrivingStateEvent;
 import android.car.drivingstate.CarUxRestrictionsManager;
 import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
+import android.car.trust.CarTrustAgentEnrollmentManager;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.PixelFormat;
@@ -1020,8 +1021,12 @@
         UserSwitcherController userSwitcherController =
                 Dependency.get(UserSwitcherController.class);
         if (userSwitcherController.useFullscreenUserSwitcher()) {
+            Car car = Car.createCar(mContext);
+            CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
+                    .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
             mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
-                    mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
+                    mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
+                    enrollmentManager, mContext);
         } else {
             super.createUserSwitcher();
         }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
new file mode 100644
index 0000000..78bb1bc
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.app.admin.DevicePolicyManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.R;
+
+/**
+ * A helper class displays an unlock dialog and receives broadcast about detecting trusted device
+ * & unlocking state to show the appropriate message on the dialog.
+ */
+class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{
+    private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName();
+
+    private final Context mContext;
+    private final WindowManager mWindowManager;
+    private final UserManager mUserManager;
+    private final WindowManager.LayoutParams mParams;
+    /**
+     * Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an
+     * activity.
+     */
+    private final View mUnlockDialog;
+    private final TextView mUnlockingText;
+    private final Button mButton;
+    private final IntentFilter mFilter;
+    private int mUid;
+    private boolean mIsDialogShowing;
+    private OnHideListener mOnHideListener;
+
+    CarTrustAgentUnlockDialogHelper(Context context) {
+        mContext = context;
+        mUserManager = mContext.getSystemService(UserManager.class);
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+        mParams = createLayoutParams();
+        mFilter = getIntentFilter();
+
+        mParams.packageName = mContext.getPackageName();
+        mParams.setTitle(mContext.getString(R.string.unlock_dialog_title));
+
+        mUnlockDialog = LayoutInflater.from(mContext).inflate(
+            R.layout.trust_agent_unlock_dialog, null);
+        mUnlockDialog.setLayoutParams(mParams);
+
+        mUnlockingText = mUnlockDialog.findViewById(R.id.unlocking_text);
+        mButton = mUnlockDialog.findViewById(R.id.enter_pin_button);
+        mButton.setOnClickListener(v -> {
+            hideUnlockDialog(/* notifyOnHideListener= */true);
+            // TODO(b/138250105) Stop unlock advertising
+        });
+
+        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (bluetoothAdapter != null
+                && bluetoothAdapter.getLeState() == BluetoothAdapter.STATE_BLE_ON) {
+            mUnlockingText.setText(R.string.unlock_dialog_message_start);
+        }
+    }
+
+    /**
+     * This filter is listening on:
+     * {@link BluetoothAdapter#ACTION_BLE_STATE_CHANGED} for starting unlock advertising;
+     * {@link Intent#ACTION_USER_UNLOCKED} for IHU unlocked
+     */
+    private IntentFilter getIntentFilter() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        return filter;
+    }
+
+    /**
+     * Show dialog for the given user
+     */
+    void showUnlockDialog(int uid, OnHideListener listener) {
+        showUnlockDialogAfterDelay(uid, 0, listener);
+    }
+
+    /**
+     * Show dialog for the given user after the certain time of delay has elapsed
+     *
+     * @param uid the user to unlock
+     * @param listener listener that listens to dialog hide
+     */
+    void showUnlockDialogAfterDelay(int uid, OnHideListener listener) {
+        long delayMillis = mContext.getResources().getInteger(R.integer.unlock_dialog_delay_ms);
+        showUnlockDialogAfterDelay(uid, delayMillis, listener);
+    }
+
+    /**
+     * Show dialog for the given user after the supplied delay has elapsed
+     */
+    private void showUnlockDialogAfterDelay(int uid, long delayMillis, OnHideListener listener) {
+        setUid(uid);
+        mOnHideListener = listener;
+        if (!mIsDialogShowing) {
+            logd("Receiver registered");
+            mContext.registerReceiverAsUser(this, UserHandle.ALL, mFilter,
+                    /* broadcastPermission= */ null,
+                    /* scheduler= */ null);
+            new Handler().postDelayed(() -> {
+                if (!mUserManager.isUserUnlocked(uid)) {
+                    logd("Showed unlock dialog for user: " + uid + " after " + delayMillis
+                            + " delay.");
+                    mWindowManager.addView(mUnlockDialog, mParams);
+                }
+            }, delayMillis);
+        }
+        mIsDialogShowing = true;
+    }
+
+    private void setUid(int uid) {
+        mUid = uid;
+        TextView userName = mUnlockDialog.findViewById(R.id.user_name);
+        userName.setText(mUserManager.getUserInfo(mUid).name);
+        ImageView avatar = mUnlockDialog.findViewById(R.id.avatar);
+        avatar.setImageBitmap(mUserManager.getUserIcon(mUid));
+        setButtonText();
+    }
+
+    private void hideUnlockDialog(boolean notifyOnHideListener) {
+        if (!mIsDialogShowing) {
+            return;
+        }
+        mWindowManager.removeView(mUnlockDialog);
+        logd("Receiver unregistered");
+        mContext.unregisterReceiver(this);
+        if (notifyOnHideListener && mOnHideListener != null) {
+            mOnHideListener.onHide();
+        }
+        mIsDialogShowing = false;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (action == null) {
+            return;
+        }
+        switch (action) {
+            case BluetoothAdapter.ACTION_BLE_STATE_CHANGED:
+                logd("Received ACTION_BLE_STATE_CHANGED");
+                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+                if (state == BluetoothAdapter.STATE_BLE_ON) {
+                    logd("Received BLE_ON");
+                    mUnlockingText.setText(R.string.unlock_dialog_message_start);
+                }
+                break;
+            case Intent.ACTION_USER_UNLOCKED:
+                int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                if (uid == mUid) {
+                    logd("IHU unlocked");
+                    hideUnlockDialog(/* notifyOnHideListener= */false);
+                } else {
+                    Log.e(TAG, "Received ACTION_USER_UNLOCKED for unexpected uid: " + uid);
+                }
+                break;
+            default:
+                Log.e(TAG, "Encountered unexpected action when attempting to set "
+                        + "unlock state message: " + action);
+        }
+    }
+
+    // Set button text based on security lock type
+    private void setButtonText() {
+        LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+        int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid);
+        switch (passwordQuality) {
+            // PIN
+            case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
+            // Pattern
+            case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+                mButton.setText(R.string.unlock_dialog_button_text_pattern);
+                break;
+            // Password
+            case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
+                mButton.setText(R.string.unlock_dialog_button_text_password);
+                break;
+            default:
+                Log.e(TAG, "Encountered unexpected security type when attempting to set "
+                        + "button text:" + passwordQuality);
+        }
+    }
+
+    private WindowManager.LayoutParams createLayoutParams() {
+        return new WindowManager.LayoutParams(
+                WindowManager.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN
+                        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
+                        | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
+                PixelFormat.TRANSLUCENT
+        );
+    }
+
+    private void logd(String message) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, message);
+        }
+    }
+
+    /**
+     * Listener used to notify when the dialog is hidden
+     */
+    interface OnHideListener {
+        void onHide();
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 0a167d9..7cd6adb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,29 +18,60 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.car.trust.CarTrustAgentEnrollmentManager;
+import android.car.userlib.CarUserManagerHelper;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewStub;
 
 import androidx.recyclerview.widget.GridLayoutManager;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
 
 /**
  * Manages the fullscreen user switcher.
  */
 public class FullscreenUserSwitcher {
+    private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
+    // Because user 0 is headless, user count for single user is 2
+    private static final int NUMBER_OF_BACKGROUND_USERS = 1;
     private final UserGridRecyclerView mUserGridView;
     private final View mParent;
     private final int mShortAnimDuration;
     private final CarStatusBar mStatusBar;
+    private final Context mContext;
+    private final UserManager mUserManager;
+    private final CarTrustAgentEnrollmentManager mEnrollmentManager;
+    private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
+    private UserGridRecyclerView.UserRecord mSelectedUser;
+    private CarUserManagerHelper mCarUserManagerHelper;
+    private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
+            }
+            showDialogForInitialUser();
+            mContext.unregisterReceiver(mUserUnlockReceiver);
+        }
+    };
 
-    public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
+
+    public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
+            CarTrustAgentEnrollmentManager enrollmentManager, Context context) {
         mStatusBar = statusBar;
         mParent = containerStub.inflate();
-        // Hide the user grid by default. It will only be made visible by clicking on a cancel
-        // button in a bouncer.
-        hide();
+        mEnrollmentManager = enrollmentManager;
+        mContext = context;
+
         View container = mParent.findViewById(R.id.container);
 
         // Initialize user grid.
@@ -50,9 +81,51 @@
         mUserGridView.setLayoutManager(layoutManager);
         mUserGridView.buildAdapter();
         mUserGridView.setUserSelectionListener(this::onUserSelected);
+        mCarUserManagerHelper = new CarUserManagerHelper(context);
+        mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
+        mUserManager = mContext.getSystemService(UserManager.class);
 
         mShortAnimDuration = container.getResources()
                 .getInteger(android.R.integer.config_shortAnimTime);
+        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+        if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
+            // User0 is unlocked, switched to the initial user
+            showDialogForInitialUser();
+        } else {
+            // listen to USER_UNLOCKED
+            mContext.registerReceiverAsUser(mUserUnlockReceiver,
+                    UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
+                    filter,
+                    /* broadcastPermission= */ null,
+                    /* scheduler */ null);
+        }
+    }
+
+    private void showDialogForInitialUser() {
+        int initialUser = mCarUserManagerHelper.getInitialUser();
+        UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
+        mSelectedUser = new UserRecord(initialUserInfo,
+                /* isStartGuestSession= */ false,
+                /* isAddUser= */ false,
+                /* isForeground= */ true);
+        // For single user without trusted device, hide the user switcher.
+        if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) {
+            dismissUserSwitcher();
+            return;
+        }
+        // Show unlock dialog for initial user
+        if (hasTrustedDevice(initialUser)) {
+            mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
+                    () -> dismissUserSwitcher());
+        }
+    }
+
+    /**
+     * Check if there is only one possible user to login in.
+     * In a Multi-User system there is always one background user (user 0)
+     */
+    private boolean hasMultipleUsers() {
+        return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1;
     }
 
     /**
@@ -77,14 +150,33 @@
     }
 
     /**
-     * Every time user clicks on an item in the switcher, we hide the switcher, either
-     * gradually or immediately.
+     * Every time user clicks on an item in the switcher, if the clicked user has no trusted device,
+     * we hide the switcher, either gradually or immediately.
      *
-     * We dismiss the entire keyguard if user clicked on the foreground user (user we're already
-     * logged in as).
+     * If the user has trusted device, we show an unlock dialog to notify user the unlock state.
+     * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
+     *
+     * We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground
+     * user (user we're already logged in as).
      */
     private void onUserSelected(UserGridRecyclerView.UserRecord record) {
-        if (record.mIsForeground) {
+        mSelectedUser = record;
+        if (hasTrustedDevice(record.mInfo.id)) {
+            mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, () -> dismissUserSwitcher());
+            return;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
+        }
+        dismissUserSwitcher();
+    }
+
+    private void dismissUserSwitcher() {
+        if (mSelectedUser == null) {
+            Log.e(TAG, "Request to dismiss user switcher, but no user selected");
+            return;
+        }
+        if (mSelectedUser.mIsForeground) {
             hide();
             mStatusBar.dismissKeyguard();
             return;
@@ -106,4 +198,8 @@
                 });
 
     }
+
+    private boolean hasTrustedDevice(int uid) {
+        return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
+    }
 }
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index 5d787f8..6e61196 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -26,7 +26,7 @@
         <item name="allowDividerAbove">true</item>
     </style>
 
-    <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay.v14.Material">
+    <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay">
         <item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item>
     </style>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 3e359d2..d3bab5f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -224,7 +224,9 @@
         mConnectivityManager = connectivityManager;
 
         // check if verbose logging developer option has been turned on or off
-        sVerboseLogging = mWifiManager != null && (mWifiManager.getVerboseLoggingLevel() > 0);
+        sVerboseLogging = Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.WIFI_VERBOSE_LOGGING_ENABLED, 0) > 0;
 
         mFilter = filter;
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2286f4c..36e945f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,7 +34,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.Validator;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.BackupUtils;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 6adb305..b7eddc2 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -18,7 +18,9 @@
 
 import android.annotation.NonNull;
 import android.os.UserHandle;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
+import android.providers.settings.ConfigSettingsProto;
 import android.providers.settings.GlobalSettingsProto;
 import android.providers.settings.SecureSettingsProto;
 import android.providers.settings.SettingProto;
@@ -28,24 +30,79 @@
 import android.util.SparseBooleanArray;
 import android.util.proto.ProtoOutputStream;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 /** @hide */
 class SettingsProtoDumpUtil {
+    private static final Map<String, Long> NAMESPACE_TO_FIELD_MAP = createNamespaceMap();
+
     private SettingsProtoDumpUtil() {}
 
+    private static Map<String, Long> createNamespaceMap() {
+        Map<String, Long> namespaceToFieldMap = new HashMap<>();
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                ConfigSettingsProto.ACTIVITY_MANAGER_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
+                ConfigSettingsProto.ACTIVITY_MANAGER_NATIVE_BOOT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_APP_COMPAT,
+                ConfigSettingsProto.APP_COMPAT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_AUTOFILL,
+                ConfigSettingsProto.AUTOFILL_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_CONNECTIVITY,
+                ConfigSettingsProto.CONNECTIVITY_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
+                ConfigSettingsProto.CONTENT_CAPTURE_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_DEX_BOOT,
+                ConfigSettingsProto.DEX_BOOT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_GAME_DRIVER,
+                ConfigSettingsProto.GAME_DRIVER_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_INPUT_NATIVE_BOOT,
+                ConfigSettingsProto.INPUT_NATIVE_BOOT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_NETD_NATIVE,
+                ConfigSettingsProto.NETD_NATIVE_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_PRIVACY,
+                ConfigSettingsProto.PRIVACY_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_ROLLBACK,
+                ConfigSettingsProto.ROLLBACK_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_ROLLBACK_BOOT,
+                ConfigSettingsProto.ROLLBACK_BOOT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_RUNTIME,
+                ConfigSettingsProto.RUNTIME_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
+                ConfigSettingsProto.RUNTIME_NATIVE_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
+                ConfigSettingsProto.RUNTIME_NATIVE_BOOT_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_STORAGE,
+                ConfigSettingsProto.STORAGE_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_SYSTEMUI,
+                ConfigSettingsProto.SYSTEMUI_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_TELEPHONY,
+                ConfigSettingsProto.TELEPHONY_SETTINGS);
+        namespaceToFieldMap.put(DeviceConfig.NAMESPACE_TEXTCLASSIFIER,
+                ConfigSettingsProto.TEXTCLASSIFIER_SETTINGS);
+        return Collections.unmodifiableMap(namespaceToFieldMap);
+    }
+
     static void dumpProtoLocked(SettingsProvider.SettingsRegistry settingsRegistry,
             ProtoOutputStream proto) {
         // Config settings
         SettingsState configSettings = settingsRegistry.getSettingsLocked(
                 SettingsProvider.SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM);
         if (configSettings != null) {
-            // TODO(b/113100523): dump configuration settings after they are added
+            dumpProtoConfigSettingsLocked(
+                    proto, SettingsServiceDumpProto.CONFIG_SETTINGS, configSettings);
         }
 
         // Global settings
         SettingsState globalSettings = settingsRegistry.getSettingsLocked(
                 SettingsProvider.SETTINGS_TYPE_GLOBAL, UserHandle.USER_SYSTEM);
         if (globalSettings != null) {
-            dumpProtoGlobalSettingsLocked(proto, SettingsServiceDumpProto.GLOBAL_SETTINGS, globalSettings);
+            dumpProtoGlobalSettingsLocked(
+                    proto, SettingsServiceDumpProto.GLOBAL_SETTINGS, globalSettings);
         }
 
         // Per-user settings
@@ -1550,9 +1607,6 @@
                 Settings.Global.WIFI_P2P_DEVICE_NAME,
                 GlobalSettingsProto.Wifi.P2P_DEVICE_NAME);
         dumpSetting(s, p,
-                Settings.Global.WIFI_REENABLE_DELAY_MS,
-                GlobalSettingsProto.Wifi.REENABLE_DELAY_MS);
-        dumpSetting(s, p,
                 Settings.Global.WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS,
                 GlobalSettingsProto.Wifi.EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS);
         dumpSetting(s, p,
@@ -1599,6 +1653,33 @@
         // Settings.Global.INSTALL_NON_MARKET_APPS intentionally excluded since it's deprecated.
     }
 
+    private static void dumpProtoConfigSettingsLocked(
+            @NonNull ProtoOutputStream p, long fieldId, @NonNull SettingsState s) {
+        Map<String, List<String>> namespaceMap = new HashMap<>();
+        final long token = p.start(fieldId);
+        s.dumpHistoricalOperations(p, ConfigSettingsProto.HISTORICAL_OPERATIONS);
+        for (String name : s.getSettingNamesLocked()) {
+            String namespace = name.substring(0, name.indexOf('/'));
+            if (NAMESPACE_TO_FIELD_MAP.containsKey(namespace)) {
+                dumpSetting(s, p, name, NAMESPACE_TO_FIELD_MAP.get(namespace));
+            } else {
+                if (!namespaceMap.containsKey(namespace)) {
+                    namespaceMap.put(namespace, new ArrayList<>());
+                }
+                namespaceMap.get(namespace).add(name);
+            }
+        }
+        for (String namespace : namespaceMap.keySet()) {
+            final long namespacesToken = p.start(ConfigSettingsProto.EXTRA_NAMESPACES);
+            p.write(ConfigSettingsProto.NamespaceProto.NAMESPACE, namespace);
+            for (String name : namespaceMap.get(namespace)) {
+                dumpSetting(s, p, name, ConfigSettingsProto.NamespaceProto.SETTINGS);
+            }
+            p.end(namespacesToken);
+        }
+        p.end(token);
+    }
+
     /** Dumps settings that use a common prefix into a repeated field. */
     private static void dumpRepeatedSetting(@NonNull SettingsState settings,
             @NonNull ProtoOutputStream proto, String settingPrefix, long fieldId) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7016d30..e492e28 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -70,7 +70,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
-import android.provider.SettingsValidators;
+import android.provider.settings.validators.Validator;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -314,11 +314,6 @@
     public boolean onCreate() {
         Settings.setInSystemServer();
 
-        // fail to boot if there're any backed up settings that don't have a non-null validator
-        ensureAllBackedUpSystemSettingsHaveValidators();
-        ensureAllBackedUpGlobalSettingsHaveValidators();
-        ensureAllBackedUpSecureSettingsHaveValidators();
-
         synchronized (mLock) {
             mUserManager = UserManager.get(getContext());
             mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -338,57 +333,6 @@
         return true;
     }
 
-    private void ensureAllBackedUpSystemSettingsHaveValidators() {
-        String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
-                Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
-
-        failToBootIfOffendersPresent(offenders, "Settings.System");
-    }
-
-    private void ensureAllBackedUpGlobalSettingsHaveValidators() {
-        String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
-                Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
-
-        failToBootIfOffendersPresent(offenders, "Settings.Global");
-    }
-
-    private void ensureAllBackedUpSecureSettingsHaveValidators() {
-        String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
-                Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
-
-        failToBootIfOffendersPresent(offenders, "Settings.Secure");
-    }
-
-    private void failToBootIfOffendersPresent(String offenders, String settingsType) {
-        if (offenders.length() > 0) {
-            throw new RuntimeException("All " + settingsType + " settings that are backed up"
-                    + " have to have a non-null validator, but those don't: " + offenders);
-        }
-    }
-
-    private String getOffenders(String[] settingsToBackup, Map<String,
-            SettingsValidators.Validator> validators) {
-        StringBuilder offenders = new StringBuilder();
-        for (String setting : settingsToBackup) {
-            if (validators.get(setting) == null) {
-                offenders.append(setting).append(" ");
-            }
-        }
-        return offenders.toString();
-    }
-
-    private final String[] concat(String[] first, String[] second) {
-        if (second == null || second.length == 0) {
-            return first;
-        }
-        final int firstLen = first.length;
-        final int secondLen = second.length;
-        String[] both = new String[firstLen + secondLen];
-        System.arraycopy(first, 0, both, 0, firstLen);
-        System.arraycopy(second, 0, both, firstLen, secondLen);
-        return both;
-    }
-
     @Override
     public Bundle call(String method, String name, Bundle args) {
         final int requestingUserId = getRequestingUserId(args);
@@ -1773,7 +1717,7 @@
     }
 
     private void validateSystemSettingValue(String name, String value) {
-        SettingsValidators.Validator validator = Settings.System.VALIDATORS.get(name);
+        Validator validator = Settings.System.VALIDATORS.get(name);
         if (validator != null && !validator.validate(value)) {
             throw new IllegalArgumentException("Invalid value: " + value
                     + " for setting: " + name);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 58e1d47..8c0108d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -352,10 +352,10 @@
 
         private final BugreportInfo mInfo;
 
-        BugreportCallbackImpl(String name) {
+        BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description) {
             // pid not used in this workflow, so setting default = 0
             mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
-                    100 /* max progress*/);
+                    100 /* max progress*/, title, description);
         }
 
         @Override
@@ -578,6 +578,8 @@
         }
         int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
                 BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+        String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+        String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
 
         ParcelFileDescriptor screenshotFd = createReadWriteFile(BUGREPORT_DIR,
                 bugreportName + ".png");
@@ -595,7 +597,8 @@
                 + " bugreport file fd: " + bugreportFd
                 + " screenshot file fd: " + screenshotFd);
 
-        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName);
+        BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName,
+                shareTitle, shareDescription);
         try {
             mBugreportManager.startBugreport(bugreportFd, screenshotFd,
                     new BugreportParams(bugreportType), executor, bugreportCallback);
@@ -982,7 +985,10 @@
             }
             screenshotFile = null;
         }
-        onBugreportFinished(id, bugreportFile, screenshotFile, info.title, info.description, max);
+        // TODO: Since we are passing id to the function, it should be able to find the info linked
+        // to the id and therefore use the value of shareTitle and shareDescription.
+        onBugreportFinished(id, bugreportFile, screenshotFile, info.shareTitle,
+                info.shareDescription, max);
     }
 
 
@@ -1844,6 +1850,14 @@
         String title;
 
         /**
+         * One-line summary of the bug; when set, will be used as the subject of the
+         * {@link Intent#ACTION_SEND_MULTIPLE} intent. This is the predefined title which is
+         * set initially when the request to take a bugreport is made. This overrides any changes
+         * in the title that the user makes after the bugreport starts.
+         */
+        String shareTitle;
+
+        /**
          * User-provided, detailed description of the bugreport; when set, will be added to the body
          * of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
          */
@@ -1906,7 +1920,9 @@
         int screenshotCounter;
 
         /**
-         * Descriptive text that will be shown to the user in the notification message.
+         * Descriptive text that will be shown to the user in the notification message. This is the
+         * predefined description which is set initially when the request to take a bugreport is
+         * made.
          */
         String shareDescription;
 
@@ -1914,18 +1930,21 @@
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
          */
         BugreportInfo(Context context, int id, int pid, String name, int max) {
-            this(context, pid, name, max);
+            this(context, pid, name, max, null, null);
             this.id = id;
         }
 
         /**
          * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
          */
-        BugreportInfo(Context context, int pid, String name, int max) {
+        BugreportInfo(Context context, int pid, String name, int max, @Nullable String shareTitle,
+                @Nullable String shareDescription) {
             this.context = context;
             this.pid = pid;
             this.name = name;
             this.max = this.realMax = max;
+            this.shareTitle = shareTitle == null ? "" : shareTitle;
+            this.shareDescription = shareDescription == null ? "" : shareDescription;
         }
 
         /**
@@ -2019,6 +2038,7 @@
                 .append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
                 .append(" addedDetailsToZip: ").append(addedDetailsToZip)
                 .append("\n\tshareDescription: ").append(shareDescription)
+                .append("\n\tshareTitle: ").append(shareTitle)
                 .toString();
         }
 
@@ -2046,6 +2066,7 @@
             finished = in.readInt() == 1;
             screenshotCounter = in.readInt();
             shareDescription = in.readString();
+            shareTitle = in.readString();
         }
 
         @Override
@@ -2071,6 +2092,7 @@
             dest.writeInt(finished ? 1 : 0);
             dest.writeInt(screenshotCounter);
             dest.writeString(shareDescription);
+            dest.writeString(shareTitle);
         }
 
         @Override
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fab7242..19e682b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1386,6 +1386,8 @@
         buttons</string>
     <string name="screen_pinning_toast_recents_invisible">To unpin this screen, touch &amp; hold Back
         and Home buttons</string>
+    <!-- Notify (in toast) user how to unpin screen in gesture navigation mode [CHAR LIMIT=NONE] -->
+    <string name="screen_pinning_toast_gesture_nav">To unpin this screen, swipe up &amp; hold</string>
     <!-- Screen pinning positive response. -->
     <string name="screen_pinning_positive">Got it</string>
     <!-- Screen pinning negative response. -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index c732584..08996c3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -188,9 +188,8 @@
         if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0) {
             return false;
         }
-        // Disable when in screen pinning, immersive, or the notifications are interactive
-        int disableFlags = SYSUI_STATE_SCREEN_PINNING
-                | SYSUI_STATE_NAV_BAR_HIDDEN
+        // Disable when in immersive, or the notifications are interactive
+        int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN
                 | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                 | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
         return (sysuiStateFlags & disableFlags) != 0;
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 0ee9bff..59270a0 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -34,6 +34,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.appops.AppOpsController;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.dock.DockManager;
@@ -200,6 +201,7 @@
 
     @Inject Lazy<ActivityStarter> mActivityStarter;
     @Inject Lazy<ActivityStarterDelegate> mActivityStarterDelegate;
+    @Inject Lazy<BroadcastDispatcher> mBroadcastDispatcher;
     @Inject Lazy<AsyncSensorManager> mAsyncSensorManager;
     @Inject Lazy<BluetoothController> mBluetoothController;
     @Inject Lazy<LocationController> mLocationController;
@@ -317,6 +319,7 @@
         mProviders.put(MAIN_HANDLER, mMainHandler::get);
         mProviders.put(ActivityStarter.class, mActivityStarter::get);
         mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
+        mProviders.put(BroadcastDispatcher.class, mBroadcastDispatcher::get);
 
         mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
 
@@ -496,6 +499,7 @@
         // Make sure that the DumpController gets added to mDependencies, as they are only added
         // with Dependency#get.
         getDependency(DumpController.class);
+        getDependency(BroadcastDispatcher.class);
 
         // If an arg is specified, try to dump the dependency
         String controller = args != null && args.length > 1
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 311ed8a..7e3b423 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -66,6 +66,7 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.volume.VolumeDialogComponent;
 
@@ -150,9 +151,9 @@
             LockscreenWallpaper lockscreenWallpaper,
             TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
             Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
-            AlarmManager alarmManager) {
+            AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
         return new ScrimController(scrimBehind, scrimInFront, scrimStateListener,
-                scrimVisibleListener, dozeParameters, alarmManager);
+                scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor);
     }
 
     public NotificationIconAreaController createNotificationIconAreaController(Context context,
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
index bc782a7..bb3bd78 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/InvocationLightsView.java
@@ -100,7 +100,9 @@
 
         int cornerRadiusBottom = DisplayUtils.getCornerRadiusBottom(context);
         int cornerRadiusTop = DisplayUtils.getCornerRadiusTop(context);
-        mViewHeight = Math.max(cornerRadiusBottom, cornerRadiusTop);
+        // ensure that height is non-zero even for square corners
+        mViewHeight = Math.max(Math.max(cornerRadiusBottom, cornerRadiusTop),
+            DisplayUtils.convertDpToPx(LIGHT_HEIGHT_DP, context));
 
         final int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
         final int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java
new file mode 100644
index 0000000..d4baefd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2019 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.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import com.android.systemui.biometrics.ui.BiometricDialogView;
+
+/**
+ * Interface for the biometric dialog UI.
+ */
+public interface BiometricDialog {
+
+    // TODO: Clean up save/restore state
+    String[] KEYS_TO_BACKUP = {
+            BiometricPrompt.KEY_TITLE,
+            BiometricPrompt.KEY_USE_DEFAULT_TITLE,
+            BiometricPrompt.KEY_SUBTITLE,
+            BiometricPrompt.KEY_DESCRIPTION,
+            BiometricPrompt.KEY_POSITIVE_TEXT,
+            BiometricPrompt.KEY_NEGATIVE_TEXT,
+            BiometricPrompt.KEY_REQUIRE_CONFIRMATION,
+            BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL,
+            BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL,
+
+            BiometricDialogView.KEY_TRY_AGAIN_VISIBILITY,
+            BiometricDialogView.KEY_CONFIRM_VISIBILITY,
+            BiometricDialogView.KEY_CONFIRM_ENABLED,
+            BiometricDialogView.KEY_STATE,
+            BiometricDialogView.KEY_ERROR_TEXT_VISIBILITY,
+            BiometricDialogView.KEY_ERROR_TEXT_STRING,
+            BiometricDialogView.KEY_ERROR_TEXT_IS_TEMPORARY,
+            BiometricDialogView.KEY_ERROR_TEXT_COLOR,
+    };
+
+    /**
+     * Show the dialog.
+     * @param wm
+     * @param skipIntroAnimation
+     */
+    void show(WindowManager wm, boolean skipIntroAnimation);
+
+    /**
+     * Dismiss the dialog without sending a callback.
+     */
+    void dismissWithoutCallback(boolean animate);
+
+    /**
+     * Dismiss the dialog. Animate away.
+     */
+    void dismissFromSystemServer();
+
+    /**
+     * Biometric authenticated. May be pending user confirmation, or completed.
+     */
+    void onAuthenticationSucceeded();
+
+    /**
+     * Authentication failed (reject, timeout). Dialog stays showing.
+     * @param failureReason
+     */
+    void onAuthenticationFailed(String failureReason);
+
+    /**
+     * Authentication rejected, or help message received.
+     * @param help
+     */
+    void onHelp(String help);
+
+    /**
+     * Authentication failed. Dialog going away.
+     * @param error
+     */
+    void onError(String error);
+
+    /**
+     * Save the current state.
+     * @param outState
+     */
+    void onSaveState(Bundle outState);
+
+    /**
+     * Restore a previous state.
+     * @param savedState
+     */
+    void restoreState(Bundle savedState);
+
+    /**
+     * Get the client's package name
+     */
+    String getOpPackageName();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index e66a8fa..a8e5722 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -16,137 +16,156 @@
 
 package com.android.systemui.biometrics;
 
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.WindowManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
-import com.android.systemui.Dependency;
 import com.android.systemui.SystemUI;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.biometrics.ui.BiometricDialogView;
 import com.android.systemui.statusbar.CommandQueue;
 
+import java.util.List;
+
 /**
- * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
- * BiometricDialogView).
+ * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
+ * appropriate biometric UI (e.g. BiometricDialogView).
  */
-public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks,
+        DialogViewCallback {
     private static final String TAG = "BiometricDialogImpl";
     private static final boolean DEBUG = true;
 
-    private static final int MSG_SHOW_DIALOG = 1;
-    private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
-    private static final int MSG_BIOMETRIC_HELP = 3;
-    private static final int MSG_BIOMETRIC_ERROR = 4;
-    private static final int MSG_HIDE_DIALOG = 5;
-    private static final int MSG_BUTTON_NEGATIVE = 6;
-    private static final int MSG_USER_CANCELED = 7;
-    private static final int MSG_BUTTON_POSITIVE = 8;
-    private static final int MSG_TRY_AGAIN_PRESSED = 9;
+    private final Injector mInjector;
 
+    // TODO: These should just be saved from onSaveState
     private SomeArgs mCurrentDialogArgs;
-    private BiometricDialogView mCurrentDialog;
+    @VisibleForTesting
+    BiometricDialog mCurrentDialog;
+
+    private Handler mHandler = new Handler(Looper.getMainLooper());
     private WindowManager mWindowManager;
-    private IBiometricServiceReceiverInternal mReceiver;
-    private boolean mDialogShowing;
-    private Callback mCallback = new Callback();
-    private WakefulnessLifecycle mWakefulnessLifecycle;
+    @VisibleForTesting
+    IActivityTaskManager mActivityTaskManager;
+    @VisibleForTesting
+    BiometricTaskStackListener mTaskStackListener;
+    @VisibleForTesting
+    IBiometricServiceReceiverInternal mReceiver;
 
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
+    public class BiometricTaskStackListener extends TaskStackListener {
         @Override
-        public void handleMessage(Message msg) {
-            switch(msg.what) {
-                case MSG_SHOW_DIALOG:
-                    handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */,
-                            null /* savedState */);
-                    break;
-                case MSG_BIOMETRIC_AUTHENTICATED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleBiometricAuthenticated((boolean) args.arg1 /* authenticated */,
-                            (String) args.arg2 /* failureReason */);
-                    args.recycle();
-                    break;
-                }
-                case MSG_BIOMETRIC_HELP: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    handleBiometricHelp((String) args.arg1 /* message */);
-                    args.recycle();
-                    break;
-                }
-                case MSG_BIOMETRIC_ERROR:
-                    handleBiometricError((String) msg.obj);
-                    break;
-                case MSG_HIDE_DIALOG:
-                    handleHideDialog((Boolean) msg.obj);
-                    break;
-                case MSG_BUTTON_NEGATIVE:
-                    handleButtonNegative();
-                    break;
-                case MSG_USER_CANCELED:
-                    handleUserCanceled();
-                    break;
-                case MSG_BUTTON_POSITIVE:
-                    handleButtonPositive();
-                    break;
-                case MSG_TRY_AGAIN_PRESSED:
-                    handleTryAgainPressed();
-                    break;
-                default:
-                    Log.w(TAG, "Unknown message: " + msg.what);
-                    break;
-            }
-        }
-    };
-
-    private class Callback implements DialogViewCallback {
-        @Override
-        public void onUserCanceled() {
-            mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
-        }
-
-        @Override
-        public void onErrorShown() {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
-                    false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
-        }
-
-        @Override
-        public void onNegativePressed() {
-            mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
-        }
-
-        @Override
-        public void onPositivePressed() {
-            mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
-        }
-
-        @Override
-        public void onTryAgainPressed() {
-            mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
+        public void onTaskStackChanged() {
+            mHandler.post(mTaskStackChangedRunnable);
         }
     }
 
-    final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
-        @Override
-        public void onStartedGoingToSleep() {
-            if (mDialogShowing) {
-                if (DEBUG) Log.d(TAG, "User canceled due to screen off");
-                mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
+    private final Runnable mTaskStackChangedRunnable = () -> {
+        if (mCurrentDialog != null) {
+            try {
+                final String clientPackage = mCurrentDialog.getOpPackageName();
+                Log.w(TAG, "Task stack changed, current client: " + clientPackage);
+                final List<ActivityManager.RunningTaskInfo> runningTasks =
+                        mActivityTaskManager.getTasks(1);
+                if (!runningTasks.isEmpty()) {
+                    final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+                    if (!topPackage.contentEquals(clientPackage)) {
+                        Log.w(TAG, "Evicting client due to: " + topPackage);
+                        mCurrentDialog.dismissWithoutCallback(true /* animate */);
+                        mCurrentDialog = null;
+                        mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+                        mReceiver = null;
+                    }
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Remote exception", e);
             }
         }
     };
 
     @Override
+    public void onTryAgainPressed() {
+        try {
+            mReceiver.onTryAgainPressed();
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException when handling try again", e);
+        }
+    }
+
+    @Override
+    public void onDismissed(@DismissedReason int reason) {
+        switch (reason) {
+            case DialogViewCallback.DISMISSED_USER_CANCELED:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+                break;
+
+            case DialogViewCallback.DISMISSED_BUTTON_NEGATIVE:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+                break;
+
+            case DialogViewCallback.DISMISSED_BUTTON_POSITIVE:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+                break;
+
+            case DialogViewCallback.DISMISSED_AUTHENTICATED:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+                break;
+
+            case DialogViewCallback.DISMISSED_ERROR:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR);
+                break;
+
+            case DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER:
+                sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+                break;
+
+            default:
+                Log.e(TAG, "Unhandled reason: " + reason);
+                break;
+        }
+    }
+
+    private void sendResultAndCleanUp(@DismissedReason int reason) {
+        if (mReceiver == null) {
+            Log.e(TAG, "Receiver is null");
+            return;
+        }
+        try {
+            mReceiver.onDialogDismissed(reason);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Remote exception", e);
+        }
+        onDialogDismissed(reason);
+    }
+
+    public static class Injector {
+        IActivityTaskManager getActivityTaskManager() {
+            return ActivityTaskManager.getService();
+        }
+    }
+
+    public BiometricDialogImpl() {
+        this(new Injector());
+    }
+
+    @VisibleForTesting
+    BiometricDialogImpl(Injector injector) {
+        mInjector = injector;
+    }
+
+    @Override
     public void start() {
         final PackageManager pm = mContext.getPackageManager();
         if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
@@ -154,30 +173,38 @@
                 || pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
             getComponent(CommandQueue.class).addCallback(this);
             mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-            mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
-            mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+            mActivityTaskManager = mInjector.getActivityTaskManager();
+
+            try {
+                mTaskStackListener = new BiometricTaskStackListener();
+                mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Unable to register task stack listener", e);
+            }
         }
     }
 
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-            int type, boolean requireConfirmation, int userId) {
+            int type, boolean requireConfirmation, int userId, String opPackageName) {
         if (DEBUG) {
             Log.d(TAG, "showBiometricDialog, type: " + type
                     + ", requireConfirmation: " + requireConfirmation);
         }
-        // Remove these messages as they are part of the previous client
-        mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
-        mHandler.removeMessages(MSG_BIOMETRIC_HELP);
-        mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
-        mHandler.removeMessages(MSG_HIDE_DIALOG);
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = bundle;
         args.arg2 = receiver;
         args.argi1 = type;
         args.arg3 = requireConfirmation;
         args.argi2 = userId;
-        mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
+        args.arg4 = opPackageName;
+
+        boolean skipAnimation = false;
+        if (mCurrentDialog != null) {
+            Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
+            skipAnimation = true;
+        }
+        showDialog(args, skipAnimation, null /* savedState */);
     }
 
     @Override
@@ -185,185 +212,111 @@
         if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated
                 + " reason: " + failureReason);
 
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = authenticated;
-        args.arg2 = failureReason;
-        mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
-    }
-
-    @Override
-    public void onBiometricHelp(String message) {
-        if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
-        SomeArgs args = SomeArgs.obtain();
-        args.arg1 = message;
-        mHandler.obtainMessage(MSG_BIOMETRIC_HELP, args).sendToTarget();
-    }
-
-    @Override
-    public void onBiometricError(String error) {
-        if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
-        mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
-    }
-
-    @Override
-    public void hideBiometricDialog() {
-        if (DEBUG) Log.d(TAG, "hideBiometricDialog");
-        mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
-    }
-
-    private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
-        mCurrentDialogArgs = args;
-        final int type = args.argi1;
-
-        // Create a new dialog but do not replace the current one yet.
-        BiometricDialogView newDialog;
-        if (type == BiometricAuthenticator.TYPE_FINGERPRINT) {
-            newDialog = new FingerprintDialogView(mContext, mCallback);
-        } else if (type == BiometricAuthenticator.TYPE_FACE) {
-            newDialog = new FaceDialogView(mContext, mCallback);
-        } else {
-            Log.e(TAG, "Unsupported type: " + type);
-            return;
-        }
-
-        if (DEBUG) Log.d(TAG, "handleShowDialog, "
-                + " savedState: " + savedState
-                + " mCurrentDialog: " + mCurrentDialog
-                + " newDialog: " + newDialog
-                + " type: " + type);
-
-        if (savedState != null) {
-            // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
-            // even though it may be removed / re-created again
-            newDialog.restoreState(savedState);
-        } else if (mCurrentDialog != null && mDialogShowing) {
-            // If somehow we're asked to show a dialog, the old one doesn't need to be animated
-            // away. This can happen if the app cancels and re-starts auth during configuration
-            // change. This is ugly because we also have to do things on onConfigurationChanged
-            // here.
-            mCurrentDialog.forceRemove();
-        }
-
-        mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
-        newDialog.setBundle((Bundle) args.arg1);
-        newDialog.setRequireConfirmation((boolean) args.arg3);
-        newDialog.setUserId(args.argi2);
-        newDialog.setSkipIntro(skipAnimation);
-        mCurrentDialog = newDialog;
-        mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
-        mDialogShowing = true;
-    }
-
-    private void handleBiometricAuthenticated(boolean authenticated, String failureReason) {
-        if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated);
-
         if (authenticated) {
-            mCurrentDialog.announceForAccessibility(
-                    mContext.getResources()
-                            .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
-            if (mCurrentDialog.requiresConfirmation()) {
-                mCurrentDialog.updateState(BiometricDialogView.STATE_PENDING_CONFIRMATION);
-            } else {
-                mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
-                mHandler.postDelayed(() -> {
-                    handleHideDialog(false /* userCanceled */);
-                }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
-            }
+            mCurrentDialog.onAuthenticationSucceeded();
         } else {
             mCurrentDialog.onAuthenticationFailed(failureReason);
         }
     }
 
-    private void handleBiometricHelp(String message) {
-        if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
-        mCurrentDialog.onHelpReceived(message);
+    @Override
+    public void onBiometricHelp(String message) {
+        if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
+
+        mCurrentDialog.onHelp(message);
     }
 
-    private void handleBiometricError(String error) {
-        if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
-        if (!mDialogShowing) {
-            if (DEBUG) Log.d(TAG, "Dialog already dismissed");
-            return;
-        }
-        mCurrentDialog.onErrorReceived(error);
+    @Override
+    public void onBiometricError(String error) {
+        if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
+        mCurrentDialog.onError(error);
     }
 
-    private void handleHideDialog(boolean userCanceled) {
-        if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
-        if (!mDialogShowing) {
-            // This can happen if there's a race and we get called from both
-            // onAuthenticated and onError, etc.
-            Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
+    @Override
+    public void hideBiometricDialog() {
+        if (DEBUG) Log.d(TAG, "hideBiometricDialog");
+
+        mCurrentDialog.dismissFromSystemServer();
+    }
+
+    private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
+        mCurrentDialogArgs = args;
+        final int type = args.argi1;
+        final Bundle biometricPromptBundle = (Bundle) args.arg1;
+        final boolean requireConfirmation = (boolean) args.arg3;
+        final int userId = args.argi2;
+        final String opPackageName = (String) args.arg4;
+
+        // Create a new dialog but do not replace the current one yet.
+        final BiometricDialog newDialog = buildDialog(
+                biometricPromptBundle,
+                requireConfirmation,
+                userId,
+                type,
+                opPackageName);
+
+        if (newDialog == null) {
+            Log.e(TAG, "Unsupported type: " + type);
             return;
         }
-        if (userCanceled) {
-            try {
-                mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException when hiding dialog", e);
-            }
+
+        if (DEBUG) {
+            Log.d(TAG, "showDialog, "
+                    + " savedState: " + savedState
+                    + " mCurrentDialog: " + mCurrentDialog
+                    + " newDialog: " + newDialog
+                    + " type: " + type);
+        }
+
+        if (savedState != null) {
+            // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
+            // even though it may be removed / re-created again
+            newDialog.restoreState(savedState);
+        } else if (mCurrentDialog != null) {
+            // If somehow we're asked to show a dialog, the old one doesn't need to be animated
+            // away. This can happen if the app cancels and re-starts auth during configuration
+            // change. This is ugly because we also have to do things on onConfigurationChanged
+            // here.
+            mCurrentDialog.dismissWithoutCallback(false /* animate */);
+        }
+
+        mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
+        mCurrentDialog = newDialog;
+        mCurrentDialog.show(mWindowManager, skipAnimation);
+    }
+
+    private void onDialogDismissed(@DismissedReason int reason) {
+        if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
+        if (mCurrentDialog == null) {
+            Log.w(TAG, "Dialog already dismissed");
         }
         mReceiver = null;
-        mDialogShowing = false;
-        mCurrentDialog.startDismiss();
-    }
-
-    private void handleButtonNegative() {
-        if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
-            return;
-        }
-        try {
-            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote exception when handling negative button", e);
-        }
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleButtonPositive() {
-        if (mReceiver == null) {
-            Log.e(TAG, "Receiver is null");
-            return;
-        }
-        try {
-            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Remote exception when handling positive button", e);
-        }
-        handleHideDialog(false /* userCanceled */);
-    }
-
-    private void handleUserCanceled() {
-        handleHideDialog(true /* userCanceled */);
-    }
-
-    private void handleTryAgainPressed() {
-        try {
-            mReceiver.onTryAgainPressed();
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException when handling try again", e);
-        }
+        mCurrentDialog = null;
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        final boolean wasShowing = mDialogShowing;
 
         // Save the state of the current dialog (buttons showing, etc)
-        final Bundle savedState = new Bundle();
         if (mCurrentDialog != null) {
+            final Bundle savedState = new Bundle();
             mCurrentDialog.onSaveState(savedState);
-        }
+            mCurrentDialog.dismissWithoutCallback(false /* animate */);
+            mCurrentDialog = null;
 
-        if (mDialogShowing) {
-            mCurrentDialog.forceRemove();
-            mDialogShowing = false;
+            showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
         }
+    }
 
-        if (wasShowing) {
-            handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
-        }
+    protected BiometricDialog buildDialog(Bundle biometricPromptBundle,
+            boolean requireConfirmation, int userId, int type, String opPackageName) {
+        return new BiometricDialogView.Builder(mContext)
+                .setCallback(this)
+                .setBiometricPromptBundle(biometricPromptBundle)
+                .setRequireConfirmation(requireConfirmation)
+                .setUserId(userId)
+                .setOpPackageName(opPackageName)
+                .build(type);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index 24fd22e..b65d1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -16,36 +16,38 @@
 
 package com.android.systemui.biometrics;
 
+import android.annotation.IntDef;
+
 /**
  * Callback interface for dialog views. These should be implemented by the controller (e.g.
  * FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView).
  */
 public interface DialogViewCallback {
-    /**
-     * Invoked when the user cancels authentication by tapping outside the prompt, etc. The dialog
-     * should be dismissed.
-     */
-    void onUserCanceled();
+
+    int DISMISSED_USER_CANCELED = 1;
+    int DISMISSED_BUTTON_NEGATIVE = 2;
+    int DISMISSED_BUTTON_POSITIVE = 3;
+
+    int DISMISSED_AUTHENTICATED = 4;
+    int DISMISSED_ERROR = 5;
+    int DISMISSED_BY_SYSTEM_SERVER = 6;
+
+    @IntDef({DISMISSED_USER_CANCELED,
+            DISMISSED_BUTTON_NEGATIVE,
+            DISMISSED_BUTTON_POSITIVE,
+            DISMISSED_AUTHENTICATED,
+            DISMISSED_ERROR,
+            DISMISSED_BY_SYSTEM_SERVER})
+    @interface DismissedReason {}
 
     /**
-     * Invoked when an error is shown. The dialog should be dismissed after a set amount of time.
+     * Invoked when the dialog is dismissed
+     * @param reason
      */
-    void onErrorShown();
+    void onDismissed(@DismissedReason int reason);
 
     /**
-     * Invoked when the negative button is pressed. The client should be notified and the dialog
-     * should be dismissed.
-     */
-    void onNegativePressed();
-
-    /**
-     * Invoked when the positive button is pressed. The client should be notified and the dialog
-     * should be dismissed.
-     */
-    void onPositivePressed();
-
-    /**
-     * Invoked when the "try again" button is pressed.
+     * Invoked when the "try again" button is clicked
      */
     void onTryAgainPressed();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
index ce67577..2b4dde5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
 
 import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
 
@@ -23,6 +23,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.Binder;
 import android.os.Bundle;
@@ -46,25 +47,30 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.BiometricDialog;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.util.leak.RotationUtils;
 
 /**
  * Abstract base class. Shows a dialog for BiometricPrompt.
  */
-public abstract class BiometricDialogView extends LinearLayout {
+public abstract class BiometricDialogView extends LinearLayout implements BiometricDialog {
 
     private static final String TAG = "BiometricDialogView";
 
-    private static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
-    private static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
-    private static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
-    private static final String KEY_STATE = "key_state";
-    private static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
-    private static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
-    private static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
-    private static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
+    public static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
+    public static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
+    public static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
+    public static final String KEY_STATE = "key_state";
+    public static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
+    public static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
+    public static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
+    public static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
 
     private static final int ANIMATION_DURATION_SHOW = 250; // ms
     private static final int ANIMATION_DURATION_AWAY = 350; // ms
@@ -77,6 +83,8 @@
     protected static final int STATE_PENDING_CONFIRMATION = 3;
     protected static final int STATE_AUTHENTICATED = 4;
 
+    @VisibleForTesting
+    final WakefulnessLifecycle mWakefulnessLifecycle;
     private final AccessibilityManager mAccessibilityManager;
     private final IBinder mWindowToken = new Binder();
     private final Interpolator mLinearOutSlowIn;
@@ -90,22 +98,30 @@
 
     protected final ViewGroup mLayout;
     protected final LinearLayout mDialog;
-    protected final TextView mTitleText;
-    protected final TextView mSubtitleText;
-    protected final TextView mDescriptionText;
-    protected final ImageView mBiometricIcon;
-    protected final TextView mErrorText;
-    protected final Button mPositiveButton;
-    protected final Button mNegativeButton;
-    protected final Button mTryAgainButton;
+    @VisibleForTesting
+    final TextView mTitleText;
+    @VisibleForTesting
+    final TextView mSubtitleText;
+    @VisibleForTesting
+    final TextView mDescriptionText;
+    @VisibleForTesting
+    final ImageView mBiometricIcon;
+    @VisibleForTesting
+    final TextView mErrorText;
+    @VisibleForTesting
+    final Button mPositiveButton;
+    @VisibleForTesting
+    final Button mNegativeButton;
+    @VisibleForTesting
+    final Button mTryAgainButton;
 
     protected final int mTextColor;
 
     private Bundle mBundle;
     private Bundle mRestoredState;
+    private String mOpPackageName;
 
     private int mState = STATE_IDLE;
-    private boolean mAnimatingAway;
     private boolean mWasForceRemoved;
     private boolean mSkipIntro;
     protected boolean mRequireConfirmation;
@@ -141,6 +157,15 @@
         }
     };
 
+    @VisibleForTesting
+    final WakefulnessLifecycle.Observer mWakefulnessObserver =
+            new WakefulnessLifecycle.Observer() {
+                @Override
+                public void onStartedGoingToSleep() {
+                    animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
+                }
+            };
+
     protected Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
@@ -155,8 +180,80 @@
         }
     };
 
-    public BiometricDialogView(Context context, DialogViewCallback callback) {
+    /**
+     * Builds the dialog with specified parameters.
+     */
+    public static class Builder {
+        public static final int TYPE_FINGERPRINT = BiometricAuthenticator.TYPE_FINGERPRINT;
+        public static final int TYPE_FACE = BiometricAuthenticator.TYPE_FACE;
+
+        private Context mContext;
+        private DialogViewCallback mCallback;
+        private Bundle mBundle;
+        private boolean mRequireConfirmation;
+        private int mUserId;
+        private String mOpPackageName;
+
+        public Builder(Context context) {
+            mContext = context;
+        }
+
+        public Builder setCallback(DialogViewCallback callback) {
+            mCallback = callback;
+            return this;
+        }
+
+        public Builder setBiometricPromptBundle(Bundle bundle) {
+            mBundle = bundle;
+            return this;
+        }
+
+        public Builder setRequireConfirmation(boolean requireConfirmation) {
+            mRequireConfirmation = requireConfirmation;
+            return this;
+        }
+
+        public Builder setUserId(int userId) {
+            mUserId = userId;
+            return this;
+        }
+
+        public Builder setOpPackageName(String opPackageName) {
+            mOpPackageName = opPackageName;
+            return this;
+        }
+
+        public BiometricDialogView build(int type) {
+            return build(type, new Injector());
+        }
+
+        public BiometricDialogView build(int type, Injector injector) {
+            BiometricDialogView dialog;
+            if (type == TYPE_FINGERPRINT) {
+                dialog = new FingerprintDialogView(mContext, mCallback, injector);
+            } else if (type == TYPE_FACE) {
+                dialog = new FaceDialogView(mContext, mCallback, injector);
+            } else {
+                return null;
+            }
+            dialog.setBundle(mBundle);
+            dialog.setRequireConfirmation(mRequireConfirmation);
+            dialog.setUserId(mUserId);
+            dialog.setOpPackageName(mOpPackageName);
+            return dialog;
+        }
+    }
+
+    public static class Injector {
+        public WakefulnessLifecycle getWakefulnessLifecycle() {
+            return Dependency.get(WakefulnessLifecycle.class);
+        }
+    }
+
+    protected BiometricDialogView(Context context, DialogViewCallback callback, Injector injector) {
         super(context);
+        mWakefulnessLifecycle = injector.getWakefulnessLifecycle();
+
         mCallback = callback;
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
@@ -178,19 +275,13 @@
         addView(mLayout);
 
         mLayout.setOnKeyListener(new View.OnKeyListener() {
-            boolean downPressed = false;
             @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
                 if (keyCode != KeyEvent.KEYCODE_BACK) {
                     return false;
                 }
-                if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
-                    downPressed = true;
-                } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                    downPressed = false;
-                } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
-                    downPressed = false;
-                    mCallback.onUserCanceled();
+                if (event.getAction() == KeyEvent.ACTION_UP) {
+                    animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
                 }
                 return true;
             }
@@ -219,16 +310,16 @@
 
         mNegativeButton.setOnClickListener((View v) -> {
             if (mState == STATE_PENDING_CONFIRMATION || mState == STATE_AUTHENTICATED) {
-                mCallback.onUserCanceled();
+                animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
             } else {
-                mCallback.onNegativePressed();
+                animateAway(DialogViewCallback.DISMISSED_BUTTON_NEGATIVE);
             }
         });
 
         mPositiveButton.setOnClickListener((View v) -> {
             updateState(STATE_AUTHENTICATED);
             mHandler.postDelayed(() -> {
-                mCallback.onPositivePressed();
+                animateAway(DialogViewCallback.DISMISSED_BUTTON_POSITIVE);
             }, getDelayAfterAuthenticatedDurationMs());
         });
 
@@ -248,21 +339,12 @@
         mLayout.requestFocus();
     }
 
-    public void onSaveState(Bundle bundle) {
-        bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
-        bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
-        bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
-        bundle.putInt(KEY_STATE, mState);
-        bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
-        bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
-        bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
-        bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
-    }
-
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
+        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+
         final ImageView backgroundView = mLayout.findViewById(R.id.background);
 
         if (mUserManager.isManagedProfile(mUserId)) {
@@ -278,6 +360,7 @@
         }
 
         mNegativeButton.setVisibility(View.VISIBLE);
+        mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
         if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
             mDialog.getLayoutParams().width = (int) mDialogWidth;
@@ -285,7 +368,6 @@
 
         if (mRestoredState == null) {
             updateState(STATE_AUTHENTICATING);
-            mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
             final int hint = getHintStringResourceId();
             if (hint != 0) {
                 mErrorText.setText(hint);
@@ -346,34 +428,49 @@
         mSkipIntro = false;
     }
 
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+    }
+
     private void setDismissesDialog(View v) {
         v.setClickable(true);
         v.setOnClickListener(v1 -> {
             if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
-                mCallback.onUserCanceled();
+                animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
             }
         });
     }
 
-    public void startDismiss() {
+    private void animateAway(@DialogViewCallback.DismissedReason int reason) {
+        animateAway(true /* sendReason */, reason);
+    }
+
+    /**
+     * Animate the dialog away
+     * @param reason one of the {@link DialogViewCallback} codes
+     */
+    private void animateAway(boolean sendReason, @DialogViewCallback.DismissedReason int reason) {
         if (!mCompletedAnimatingIn) {
             Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn");
             mPendingDismissDialog = true;
             return;
         }
 
-        mAnimatingAway = true;
-
         // This is where final cleanup should occur.
         final Runnable endActionRunnable = new Runnable() {
             @Override
             public void run() {
                 mWindowManager.removeView(BiometricDialogView.this);
-                mAnimatingAway = false;
                 // Set the icons / text back to normal state
                 handleResetMessage();
                 showTryAgainButton(false /* show */);
                 updateState(STATE_IDLE);
+                if (sendReason) {
+                    mCallback.onDismissed(reason);
+                }
             }
         };
 
@@ -398,47 +495,30 @@
     }
 
     /**
-     * Force remove the window, cancelling any animation that's happening. This should only be
-     * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
-     * will cause the dialog to show without an animation the next time it's attached.
-     */
-    public void forceRemove() {
-        mLayout.animate().cancel();
-        mDialog.animate().cancel();
-        mWindowManager.removeView(BiometricDialogView.this);
-        mAnimatingAway = false;
-        mWasForceRemoved = true;
-    }
-
-    /**
      * Skip the intro animation
      */
-    public void setSkipIntro(boolean skip) {
+    private void setSkipIntro(boolean skip) {
         mSkipIntro = skip;
     }
 
-    public boolean isAnimatingAway() {
-        return mAnimatingAway;
-    }
-
-    public void setBundle(Bundle bundle) {
+    private void setBundle(Bundle bundle) {
         mBundle = bundle;
     }
 
-    public void setRequireConfirmation(boolean requireConfirmation) {
+    private void setRequireConfirmation(boolean requireConfirmation) {
         mRequireConfirmation = requireConfirmation;
     }
 
-    public boolean requiresConfirmation() {
+    protected boolean requiresConfirmation() {
         return mRequireConfirmation;
     }
 
-    public void setUserId(int userId) {
+    private void setUserId(int userId) {
         mUserId = userId;
     }
 
-    public ViewGroup getLayout() {
-        return mLayout;
+    private void setOpPackageName(String opPackageName) {
+        mOpPackageName = opPackageName;
     }
 
     // Shows an error/help message
@@ -452,17 +532,63 @@
                 BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
+    @Override
+    public void show(WindowManager wm, boolean skipIntroAnimation) {
+        setSkipIntro(skipIntroAnimation);
+        wm.addView(this, getLayoutParams(mWindowToken));
+    }
+
+    /**
+     * Force remove the window, cancelling any animation that's happening. This should only be
+     * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+     * will cause the dialog to show without an animation the next time it's attached.
+     */
+    @Override
+    public void dismissWithoutCallback(boolean animate) {
+        if (animate) {
+            animateAway(false /* sendReason */, 0 /* reason */);
+        } else {
+            mLayout.animate().cancel();
+            mDialog.animate().cancel();
+            mWindowManager.removeView(BiometricDialogView.this);
+            mWasForceRemoved = true;
+        }
+    }
+
+    @Override
+    public void dismissFromSystemServer() {
+        animateAway(DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER);
+    }
+
+    @Override
+    public void onAuthenticationSucceeded() {
+        announceForAccessibility(getResources().getText(getAuthenticatedAccessibilityResourceId()));
+
+        if (requiresConfirmation()) {
+            updateState(STATE_PENDING_CONFIRMATION);
+        } else {
+            mHandler.postDelayed(() -> {
+                animateAway(DialogViewCallback.DISMISSED_AUTHENTICATED);
+            }, getDelayAfterAuthenticatedDurationMs());
+
+            updateState(STATE_AUTHENTICATED);
+        }
+    }
+
+
+    @Override
+    public void onAuthenticationFailed(String message) {
+        updateState(STATE_ERROR);
+        showTemporaryMessage(message);
+    }
+
     /**
      * Transient help message (acquire) is received, dialog stays showing. Sensor stays in
      * "authenticating" state.
      * @param message
      */
-    public void onHelpReceived(String message) {
-        updateState(STATE_ERROR);
-        showTemporaryMessage(message);
-    }
-
-    public void onAuthenticationFailed(String message) {
+    @Override
+    public void onHelp(String message) {
         updateState(STATE_ERROR);
         showTemporaryMessage(message);
     }
@@ -471,14 +597,61 @@
      * Hard error is received, dialog will be dismissed soon.
      * @param error
      */
-    public void onErrorReceived(String error) {
+    @Override
+    public void onError(String error) {
         updateState(STATE_ERROR);
         showTemporaryMessage(error);
         showTryAgainButton(false /* show */);
-        mCallback.onErrorShown(); // TODO: Split between fp and face
+
+        mHandler.postDelayed(() -> {
+            animateAway(DialogViewCallback.DISMISSED_ERROR);
+        }, BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
-    public void updateState(int newState) {
+
+    @Override
+    public void onSaveState(Bundle bundle) {
+        bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
+        bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
+        bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
+        bundle.putInt(KEY_STATE, mState);
+        bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
+        bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
+        bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
+        bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
+    }
+
+    @Override
+    public void restoreState(Bundle bundle) {
+        mRestoredState = bundle;
+        final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
+        mTryAgainButton.setVisibility(tryAgainVisibility);
+        final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
+        mPositiveButton.setVisibility(confirmVisibility);
+        final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
+        mPositiveButton.setEnabled(confirmEnabled);
+        mState = bundle.getInt(KEY_STATE);
+        mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+        mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+        final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
+        mErrorText.setVisibility(errorTextVisibility);
+        if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
+                || confirmVisibility == View.INVISIBLE) {
+            announceAccessibilityEvent();
+        }
+        mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
+        if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
+                    BiometricPrompt.HIDE_DIALOG_DELAY);
+        }
+    }
+
+    @Override
+    public String getOpPackageName() {
+        return mOpPackageName;
+    }
+
+    protected void updateState(int newState) {
         if (newState == STATE_PENDING_CONFIRMATION) {
             mHandler.removeMessages(MSG_RESET_MESSAGE);
             mErrorText.setTextColor(mTextColor);
@@ -505,48 +678,24 @@
         mState = newState;
     }
 
-    public void showTryAgainButton(boolean show) {
+    protected void showTryAgainButton(boolean show) {
     }
 
-    public void onDialogAnimatedIn() {
+    protected void onDialogAnimatedIn() {
         mCompletedAnimatingIn = true;
 
         if (mPendingDismissDialog) {
             Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
-            startDismiss();
+            animateAway(false /* sendReason */, 0);
             mPendingDismissDialog = false;
         }
     }
 
-    public void restoreState(Bundle bundle) {
-        mRestoredState = bundle;
-        final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
-        mTryAgainButton.setVisibility(tryAgainVisibility);
-        final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
-        mPositiveButton.setVisibility(confirmVisibility);
-        final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
-        mPositiveButton.setEnabled(confirmEnabled);
-        mState = bundle.getInt(KEY_STATE);
-        mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
-        mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
-        final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
-        mErrorText.setVisibility(errorTextVisibility);
-        if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
-                || confirmVisibility == View.INVISIBLE) {
-            announceAccessibilityEvent();
-        }
-        mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
-        if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
-                    BiometricPrompt.HIDE_DIALOG_DELAY);
-        }
-    }
-
-    protected int getState() {
-        return mState;
-    }
-
-    public WindowManager.LayoutParams getLayoutParams() {
+    /**
+     * @param windowToken token for the window
+     * @return
+     */
+    public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
@@ -555,7 +704,7 @@
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("BiometricDialogView");
-        lp.token = mWindowToken;
+        lp.token = windowToken;
         return lp;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
index ae6cb5c..bd87148 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -33,7 +33,9 @@
 import android.view.View;
 import android.view.ViewOutlineProvider;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
+import com.android.systemui.biometrics.DialogViewCallback;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
@@ -52,7 +54,8 @@
     private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
 
     private static final int SIZE_UNKNOWN = 0;
-    private static final int SIZE_SMALL = 1;
+    @VisibleForTesting
+    static final int SIZE_SMALL = 1;
     private static final int SIZE_GROWING = 2;
     private static final int SIZE_BIG = 3;
 
@@ -152,13 +155,13 @@
         announceAccessibilityEvent();
     };
 
-    public FaceDialogView(Context context,
-            DialogViewCallback callback) {
-        super(context, callback);
+    protected FaceDialogView(Context context, DialogViewCallback callback, Injector injector) {
+        super(context, callback, injector);
         mIconController = new IconController();
     }
 
-    private void updateSize(int newSize) {
+    @VisibleForTesting
+    void updateSize(int newSize) {
         final float padding = dpToPixels(IMPLICIT_Y_PADDING);
         final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
 
@@ -339,8 +342,8 @@
     }
 
     @Override
-    public void onErrorReceived(String error) {
-        super.onErrorReceived(error);
+    public void onError(String error) {
+        super.onError(error);
         // All error messages will cause the dialog to go from small -> big. Error messages
         // are messages such as lockout, auth failed, etc.
         if (mSize == SIZE_SMALL) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
index 183933e..e597080 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
@@ -11,10 +11,10 @@
  * 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
+ * limitations under the License.
  */
 
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
 
 import android.content.Context;
 import android.graphics.drawable.AnimatedVectorDrawable;
@@ -22,6 +22,7 @@
 import android.util.Log;
 
 import com.android.systemui.R;
+import com.android.systemui.biometrics.DialogViewCallback;
 
 /**
  * This class loads the view for the system-provided dialog. The view consists of:
@@ -32,9 +33,9 @@
 
     private static final String TAG = "FingerprintDialogView";
 
-    public FingerprintDialogView(Context context,
-            DialogViewCallback callback) {
-        super(context, callback);
+    protected FingerprintDialogView(Context context, DialogViewCallback callback,
+            Injector injector) {
+        super(context, callback, injector);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
new file mode 100644
index 0000000..f0e8c16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2019 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.broadcast
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.IntentFilter
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.os.UserHandle
+import android.util.Log
+import android.util.SparseArray
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Dependency.BG_LOOPER_NAME
+import com.android.systemui.Dependency.MAIN_HANDLER_NAME
+import com.android.systemui.Dumpable
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Named
+import javax.inject.Singleton
+
+data class ReceiverData(
+    val receiver: BroadcastReceiver,
+    val filter: IntentFilter,
+    val handler: Handler,
+    val user: UserHandle
+)
+
+private const val MSG_ADD_RECEIVER = 0
+private const val MSG_REMOVE_RECEIVER = 1
+private const val MSG_REMOVE_RECEIVER_FOR_USER = 2
+private const val TAG = "BroadcastDispatcher"
+private const val DEBUG = false
+
+/**
+ * SystemUI master Broadcast Dispatcher.
+ *
+ * This class allows [BroadcastReceiver] to register and centralizes registrations to [Context]
+ * from SystemUI. That way the number of calls to [BroadcastReceiver.onReceive] can be reduced for
+ * a given broadcast.
+ *
+ * Use only for IntentFilters with actions and optionally categories. It does not support,
+ * permissions, schemes or data types. Cannot be used for getting sticky broadcasts.
+ */
+@Singleton
+open class BroadcastDispatcher @Inject constructor (
+    private val context: Context,
+    @Named(MAIN_HANDLER_NAME) private val mainHandler: Handler,
+    @Named(BG_LOOPER_NAME) private val bgLooper: Looper
+) : Dumpable {
+
+    // Only modify in BG thread
+    private val receiversByUser = SparseArray<UserBroadcastDispatcher>(20)
+
+    /**
+     * Register a receiver for broadcast with the dispatcher
+     *
+     * @param receiver A receiver to dispatch the [Intent]
+     * @param filter A filter to determine what broadcasts should be dispatched to this receiver.
+     *               It will only take into account actions and categories for filtering.
+     * @param handler A handler to dispatch [BroadcastReceiver.onReceive]. By default, it is the
+     *                main handler.
+     * @param user A user handle to determine which broadcast should be dispatched to this receiver.
+     *             By default, it is the current user.
+     */
+    @JvmOverloads
+    fun registerReceiver(
+        receiver: BroadcastReceiver,
+        filter: IntentFilter,
+        handler: Handler = mainHandler,
+        user: UserHandle = context.user
+    ) {
+        this.handler.obtainMessage(MSG_ADD_RECEIVER, ReceiverData(receiver, filter, handler, user))
+                .sendToTarget()
+    }
+
+    /**
+     * Unregister receiver for all users.
+     * <br>
+     * This will remove every registration of [receiver], not those done just with [UserHandle.ALL].
+     *
+     * @param receiver The receiver to unregister. It will be unregistered for all users.
+     */
+    fun unregisterReceiver(receiver: BroadcastReceiver) {
+        handler.obtainMessage(MSG_REMOVE_RECEIVER, receiver).sendToTarget()
+    }
+
+    /**
+     * Unregister receiver for a particular user.
+     *
+     * @param receiver The receiver to unregister. It will be unregistered for all users.
+     * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL].
+     */
+    fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) {
+        handler.obtainMessage(MSG_REMOVE_RECEIVER_FOR_USER, user.identifier, 0, receiver)
+                .sendToTarget()
+    }
+
+    @VisibleForTesting
+    protected open fun createUBRForUser(userId: Int) =
+            UserBroadcastDispatcher(context, userId, mainHandler, bgLooper)
+
+    override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) {
+        pw?.println("Broadcast dispatcher:")
+        for (index in 0 until receiversByUser.size()) {
+            pw?.println("  User ${receiversByUser.keyAt(index)}")
+            receiversByUser.valueAt(index).dump(fd, pw, args)
+        }
+    }
+
+    private val handler = object : Handler(bgLooper) {
+        override fun handleMessage(msg: Message) {
+            when (msg.what) {
+                MSG_ADD_RECEIVER -> {
+                    val data = msg.obj as ReceiverData
+                    val userId = data.user.identifier
+                    if (userId < UserHandle.USER_ALL) {
+                        if (DEBUG) Log.w(TAG, "Register receiver for invalid user: $userId")
+                        return
+                    }
+                    val uBR = receiversByUser.get(userId, createUBRForUser(userId))
+                    receiversByUser.put(userId, uBR)
+                    uBR.registerReceiver(data)
+                }
+
+                MSG_REMOVE_RECEIVER -> {
+                    for (it in 0 until receiversByUser.size()) {
+                        receiversByUser.valueAt(it).unregisterReceiver(msg.obj as BroadcastReceiver)
+                    }
+                }
+
+                MSG_REMOVE_RECEIVER_FOR_USER -> {
+                    receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver)
+                }
+
+                else -> super.handleMessage(msg)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
new file mode 100644
index 0000000..d44b63e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2019 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.broadcast
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.os.UserHandle
+import android.util.ArrayMap
+import android.util.ArraySet
+import android.util.Log
+import com.android.systemui.Dumpable
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.util.concurrent.atomic.AtomicBoolean
+
+private const val MSG_REGISTER_RECEIVER = 0
+private const val MSG_UNREGISTER_RECEIVER = 1
+private const val TAG = "UniversalReceiver"
+private const val DEBUG = false
+
+/**
+ * Broadcast dispatcher for a given user registration [userId].
+ *
+ * Created by [BroadcastDispatcher] as needed by users. The value of [userId] can be
+ * [UserHandle.USER_ALL].
+ */
+class UserBroadcastDispatcher(
+    private val context: Context,
+    private val userId: Int,
+    private val mainHandler: Handler,
+    private val bgLooper: Looper
+) : BroadcastReceiver(), Dumpable {
+
+    private val bgHandler = object : Handler(bgLooper) {
+        override fun handleMessage(msg: Message) {
+            when (msg.what) {
+                MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData)
+                MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver)
+                else -> Unit
+            }
+        }
+    }
+
+    private val registered = AtomicBoolean(false)
+
+    internal fun isRegistered() = registered.get()
+
+    private val registerReceiver = Runnable {
+        val categories = mutableSetOf<String>()
+        receiverToReceiverData.values.flatten().forEach {
+            it.filter.categoriesIterator()?.asSequence()?.let {
+                categories.addAll(it)
+            }
+        }
+        val intentFilter = IntentFilter().apply {
+            actionsToReceivers.keys.forEach { addAction(it) }
+            categories.forEach { addCategory(it) }
+        }
+
+        if (registered.get()) {
+            context.unregisterReceiver(this)
+            registered.set(false)
+        }
+        // Short interval without receiver, this can be problematic
+        if (intentFilter.countActions() > 0 && !registered.get()) {
+            context.registerReceiverAsUser(
+                    this,
+                    UserHandle.of(userId),
+                    intentFilter,
+                    null,
+                    bgHandler)
+            registered.set(true)
+        }
+    }
+
+    // Only modify in BG thread
+    private val actionsToReceivers = ArrayMap<String, MutableSet<ReceiverData>>()
+    private val receiverToReceiverData = ArrayMap<BroadcastReceiver, MutableSet<ReceiverData>>()
+
+    override fun onReceive(context: Context, intent: Intent) {
+        bgHandler.post(HandleBroadcastRunnable(actionsToReceivers, context, intent))
+    }
+
+    /**
+     * Register a [ReceiverData] for this user.
+     */
+    fun registerReceiver(receiverData: ReceiverData) {
+        bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, receiverData).sendToTarget()
+    }
+
+    /**
+     * Unregister a given [BroadcastReceiver] for this user.
+     */
+    fun unregisterReceiver(receiver: BroadcastReceiver) {
+        bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget()
+    }
+
+    private fun handleRegisterReceiver(receiverData: ReceiverData) {
+        if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}")
+        receiverToReceiverData.getOrPut(receiverData.receiver, { ArraySet() }).add(receiverData)
+        var changed = false
+        // Index the BroadcastReceiver by all its actions, that way it's easier to dispatch given
+        // a received intent.
+        receiverData.filter.actionsIterator().forEach {
+            actionsToReceivers.getOrPut(it) {
+                changed = true
+                ArraySet()
+            }.add(receiverData)
+        }
+        if (changed) {
+            mainHandler.post(registerReceiver)
+        }
+    }
+
+    private fun handleUnregisterReceiver(receiver: BroadcastReceiver) {
+        if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver")
+        val actions = receiverToReceiverData.getOrElse(receiver) { return }
+                .flatMap { it.filter.actionsIterator().asSequence().asIterable() }.toSet()
+        receiverToReceiverData.get(receiver)?.clear()
+        var changed = false
+        actions.forEach { action ->
+            actionsToReceivers.get(action)?.removeIf { it.receiver == receiver }
+            if (actionsToReceivers.get(action)?.isEmpty() ?: false) {
+                changed = true
+                actionsToReceivers.remove(action)
+            }
+        }
+        if (changed) {
+            mainHandler.post(registerReceiver)
+        }
+    }
+
+    override fun dump(fd: FileDescriptor?, pw: PrintWriter?, args: Array<out String>?) {
+        pw?.println("  Registered=${registered.get()}")
+        actionsToReceivers.forEach { (action, list) ->
+            pw?.println("    $action:")
+            list.forEach { pw?.println("      ${it.receiver}") }
+        }
+    }
+
+    private class HandleBroadcastRunnable(
+        val actionsToReceivers: Map<String, Set<ReceiverData>>,
+        val context: Context,
+        val intent: Intent
+    ) : Runnable {
+        override fun run() {
+            if (DEBUG) Log.w(TAG, "Dispatching $intent")
+            actionsToReceivers.get(intent.action)
+                    ?.filter {
+                        it.filter.hasAction(intent.action) &&
+                            it.filter.matchCategories(intent.categories) == null }
+                    ?.forEach {
+                        it.handler.post {
+                            if (DEBUG) Log.w(TAG, "Dispatching to ${it.receiver}")
+                            it.receiver.onReceive(context, intent)
+                        }
+                    }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
index c58b7db..82ae30a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -39,8 +39,8 @@
     // most swipes will follow somewhat of a 'C' or 'S' shape, we allow more deviance along the
     // `SECONDARY` axis.
     private static final float MAX_X_PRIMARY_DEVIANCE = .05f;
-    private static final float MAX_Y_PRIMARY_DEVIANCE = .05f;
-    private static final float MAX_X_SECONDARY_DEVIANCE = .3f;
+    private static final float MAX_Y_PRIMARY_DEVIANCE = .1f;
+    private static final float MAX_X_SECONDARY_DEVIANCE = .6f;
     private static final float MAX_Y_SECONDARY_DEVIANCE = .3f;
 
     private final float mMaxXPrimaryDeviance;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
index e6a9e47..e5a54b8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeAuthRemover.java
@@ -25,7 +25,7 @@
  */
 public class DozeAuthRemover implements DozeMachine.Part {
 
-    KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
     public DozeAuthRemover(Context context) {
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 1bc7e63..86d4a48 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -30,6 +30,7 @@
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.AsyncSensorManager;
@@ -47,6 +48,7 @@
         SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
         AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
         DockManager dockManager = Dependency.get(DockManager.class);
+        WakefulnessLifecycle wakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
 
         DozeHost host = getHost(dozeService);
         AmbientDisplayConfiguration config = new AmbientDisplayConfiguration(context);
@@ -61,7 +63,8 @@
         wrappedService = DozeSuspendScreenStatePreventingAdapter.wrapIfNeeded(wrappedService,
                 params);
 
-        DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock);
+        DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock,
+                wakefulnessLifecycle);
         machine.setParts(new DozeMachine.Part[]{
                 new DozePauser(handler, machine, alarmManager, params.getPolicy()),
                 new DozeFalsingManagerAdapter(falsingManager),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 8bf2256..93a51cc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -24,6 +24,8 @@
 import android.view.Display;
 
 import com.android.internal.util.Preconditions;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -118,6 +120,7 @@
     private final Service mDozeService;
     private final WakeLock mWakeLock;
     private final AmbientDisplayConfiguration mConfig;
+    private final WakefulnessLifecycle mWakefulnessLifecycle;
     private Part[] mParts;
 
     private final ArrayList<State> mQueuedRequests = new ArrayList<>();
@@ -126,9 +129,10 @@
     private boolean mWakeLockHeldForCurrentState = false;
 
     public DozeMachine(Service service, AmbientDisplayConfiguration config,
-            WakeLock wakeLock) {
+            WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle) {
         mDozeService = service;
         mConfig = config;
+        mWakefulnessLifecycle = wakefulnessLifecycle;
         mWakeLock = wakeLock;
     }
 
@@ -334,9 +338,18 @@
         switch (state) {
             case INITIALIZED:
             case DOZE_PULSE_DONE:
-                transitionTo(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)
-                        ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE,
-                        DozeLog.PULSE_REASON_NONE);
+                final State nextState;
+                @Wakefulness int wakefulness = mWakefulnessLifecycle.getWakefulness();
+                if (wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE
+                        || wakefulness == WakefulnessLifecycle.WAKEFULNESS_WAKING) {
+                    nextState = State.FINISH;
+                } else if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+                    nextState = State.DOZE_AOD;
+                } else {
+                    nextState = State.DOZE;
+                }
+
+                transitionTo(nextState, DozeLog.PULSE_REASON_NONE);
                 break;
             default:
                 break;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index f79bb3a..026a625 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -288,6 +288,7 @@
         final AlarmTimeout mCooldownTimer;
         final AlwaysOnDisplayPolicy mPolicy;
         final Sensor mSensor;
+        final boolean mUsingBrightnessSensor;
 
         public ProxSensor(AlwaysOnDisplayPolicy policy) {
             mPolicy = policy;
@@ -298,6 +299,7 @@
             // if available.
             Sensor sensor = DozeSensors.findSensorWithType(mSensorManager,
                     mContext.getString(R.string.doze_brightness_sensor_type));
+            mUsingBrightnessSensor = sensor != null;
             if (sensor == null) {
                 sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
             }
@@ -327,8 +329,7 @@
                 return;
             }
             if (register) {
-                mRegistered = mSensorManager.registerListener(this,
-                        mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
+                mRegistered = mSensorManager.registerListener(this, mSensor,
                         SensorManager.SENSOR_DELAY_NORMAL, mHandler);
             } else {
                 mSensorManager.unregisterListener(this);
@@ -341,7 +342,13 @@
         public void onSensorChanged(android.hardware.SensorEvent event) {
             if (DEBUG) Log.d(TAG, "onSensorChanged " + event);
 
-            mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
+            if (mUsingBrightnessSensor) {
+                // The custom brightness sensor is gated by the proximity sensor and will return 0
+                // whenever prox is covered.
+                mCurrentlyFar = event.values[0] > 0;
+            } else {
+                mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
+            }
             mProxCallback.accept(mCurrentlyFar);
 
             long now = SystemClock.elapsedRealtime();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index 52a0214..d17f2f6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.keyguard;
 
+import android.annotation.IntDef;
 import android.os.Trace;
 
 import com.android.systemui.Dumpable;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -33,6 +36,15 @@
 public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observer> implements
         Dumpable {
 
+    @IntDef(prefix = { "WAKEFULNESS_" }, value = {
+            WAKEFULNESS_ASLEEP,
+            WAKEFULNESS_WAKING,
+            WAKEFULNESS_AWAKE,
+            WAKEFULNESS_GOING_TO_SLEEP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Wakefulness {}
+
     public static final int WAKEFULNESS_ASLEEP = 0;
     public static final int WAKEFULNESS_WAKING = 1;
     public static final int WAKEFULNESS_AWAKE = 2;
@@ -44,7 +56,7 @@
     public WakefulnessLifecycle() {
     }
 
-    public int getWakefulness() {
+    public @Wakefulness int getWakefulness() {
         return mWakefulness;
     }
 
@@ -86,7 +98,7 @@
         pw.println("  mWakefulness=" + mWakefulness);
     }
 
-    private void setWakefulness(int wakefulness) {
+    private void setWakefulness(@Wakefulness int wakefulness) {
         mWakefulness = wakefulness;
         Trace.traceCounter(Trace.TRACE_TAG_APP, "wakefulness", wakefulness);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 991d9fa..0403a05 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -248,7 +248,9 @@
         int numPages = Math.max(nTiles / mPages.get(0).maxTiles(), 1);
 
         // Add one more not full page if needed
-        numPages += (nTiles % mPages.get(0).maxTiles() == 0 ? 0 : 1);
+        if (nTiles > numPages * mPages.get(0).maxTiles()) {
+            numPages++;
+        }
 
         final int NP = mPages.size();
         for (int i = 0; i < NP; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index c209b31..83b000d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -296,10 +296,13 @@
         mY = y - containerLocation[1];
     }
 
-    private final Callback mKeyguardCallback = () -> {
-        if (!isAttachedToWindow()) return;
-        if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
-            hide();
+    private final Callback mKeyguardCallback = new Callback() {
+        @Override
+        public void onKeyguardShowingChanged() {
+            if (!isAttachedToWindow()) return;
+            if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
+                hide();
+            }
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 001e094..16a3975 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -73,6 +73,7 @@
         if (listening) {
             refreshState();
         }
+        mHotspotController.handleSetListening(listening);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index fa0fe13..134d4b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -271,7 +271,7 @@
         default void onRotationProposal(int rotation, boolean isValid) { }
 
         default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-                int type, boolean requireConfirmation, int userId) { }
+                int type, boolean requireConfirmation, int userId, String opPackageName) { }
         default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
         default void onBiometricHelp(String message) { }
         default void onBiometricError(String error) { }
@@ -741,7 +741,7 @@
 
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-            int type, boolean requireConfirmation, int userId) {
+            int type, boolean requireConfirmation, int userId, String opPackageName) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
@@ -749,6 +749,7 @@
             args.argi1 = type;
             args.arg3 = requireConfirmation;
             args.argi2 = userId;
+            args.arg4 = opPackageName;
             mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
                     .sendToTarget();
         }
@@ -1036,7 +1037,8 @@
                                 (IBiometricServiceReceiverInternal) someArgs.arg2,
                                 someArgs.argi1 /* type */,
                                 (boolean) someArgs.arg3 /* requireConfirmation */,
-                                someArgs.argi2 /* userId */);
+                                someArgs.argi2 /* userId */,
+                                (String) someArgs.arg4 /* opPackageName */);
                     }
                     someArgs.recycle();
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 48d6de9..276afa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -41,7 +41,6 @@
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.phone.ShadeController
-import com.android.systemui.statusbar.policy.HeadsUpManager
 
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -52,11 +51,13 @@
  */
 @Singleton
 class PulseExpansionHandler @Inject
-constructor(context: Context,
-            private val wakeUpCoordinator: NotificationWakeUpCoordinator,
-            private val bypassController: KeyguardBypassController,
-            private val headsUpManager: HeadsUpManagerPhone,
-            private val roundnessManager: NotificationRoundnessManager) : Gefingerpoken {
+constructor(
+    context: Context,
+    private val wakeUpCoordinator: NotificationWakeUpCoordinator,
+    private val bypassController: KeyguardBypassController,
+    private val headsUpManager: HeadsUpManagerPhone,
+    private val roundnessManager: NotificationRoundnessManager
+) : Gefingerpoken {
     companion object {
         private val RUBBERBAND_FACTOR_STATIC = 0.25f
         private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
@@ -124,8 +125,8 @@
     }
 
     private fun maybeStartExpansion(event: MotionEvent): Boolean {
-        if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded
-                || bouncerShowing) {
+        if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded ||
+                bouncerShowing) {
             return false
         }
         if (velocityTracker == null) {
@@ -160,18 +161,18 @@
             }
 
             MotionEvent.ACTION_UP -> {
-                recycleVelocityTracker();
+                recycleVelocityTracker()
             }
 
             MotionEvent.ACTION_CANCEL -> {
-                recycleVelocityTracker();
+                recycleVelocityTracker()
             }
         }
         return false
     }
 
     private fun recycleVelocityTracker() {
-        velocityTracker?.recycle();
+        velocityTracker?.recycle()
         velocityTracker = null
     }
 
@@ -216,7 +217,7 @@
                     "com.android.systemui:PULSEDRAG")
         }
         shadeController.goToLockedShade(mStartingChild)
-        leavingLockscreen = true;
+        leavingLockscreen = true
         isExpanding = false
         if (mStartingChild is ExpandableNotificationRow) {
             val row = mStartingChild as ExpandableNotificationRow?
@@ -227,7 +228,7 @@
     private fun updateExpansionHeight(height: Float) {
         var expansionHeight = max(height, 0.0f)
         if (!mReachedWakeUpHeight && height > mWakeUpHeight) {
-            mReachedWakeUpHeight = true;
+            mReachedWakeUpHeight = true
         }
         if (mStartingChild != null) {
             val child = mStartingChild!!
@@ -317,9 +318,11 @@
         } else null
     }
 
-    fun setUp(stackScroller: NotificationStackScrollLayout,
-              expansionCallback: ExpansionCallback,
-              shadeController: ShadeController) {
+    fun setUp(
+        stackScroller: NotificationStackScrollLayout,
+        expansionCallback: ExpansionCallback,
+        shadeController: ShadeController
+    ) {
         this.expansionCallback = expansionCallback
         this.shadeController = shadeController
         this.stackScroller = stackScroller
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 6a74779..3314e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -34,6 +34,7 @@
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.MathUtils;
+import android.util.StatsLog;
 import android.view.Gravity;
 import android.view.IPinnedStackController;
 import android.view.IPinnedStackListener;
@@ -107,7 +108,11 @@
                 public void onSystemGestureExclusionChanged(int displayId,
                         Region systemGestureExclusion, Region unrestrictedOrNull) {
                     if (displayId == mDisplayId) {
-                        mMainExecutor.execute(() -> mExcludeRegion.set(systemGestureExclusion));
+                        mMainExecutor.execute(() -> {
+                            mExcludeRegion.set(systemGestureExclusion);
+                            mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
+                                    ? unrestrictedOrNull : systemGestureExclusion);
+                        });
                     }
                 }
             };
@@ -121,6 +126,8 @@
     private final Executor mMainExecutor;
 
     private final Region mExcludeRegion = new Region();
+    private final Region mUnrestrictedExcludeRegion = new Region();
+
     // The edge width where touch down is allowed
     private int mEdgeWidth;
     // The slop to distinguish between horizontal and vertical motion
@@ -139,6 +146,7 @@
     private final PointF mDownPoint = new PointF();
     private boolean mThresholdCrossed = false;
     private boolean mAllowGesture = false;
+    private boolean mInRejectedExclusion = false;
     private boolean mIsOnLeftEdge;
 
     private int mImeHeight = 0;
@@ -297,6 +305,7 @@
                             return mSamplingRect;
                         }
                     });
+            mRegionSamplingHelper.setWindowVisible(true);
         }
     }
 
@@ -318,6 +327,12 @@
         if (isInExcludedRegion) {
             mOverviewProxyService.notifyBackAction(false /* completed */, -1, -1,
                     false /* isButton */, !mIsOnLeftEdge);
+            StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED,
+                    StatsLog.BACK_GESTURE__TYPE__INCOMPLETE_EXCLUDED, y,
+                    mIsOnLeftEdge ? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
+                            StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
+        } else {
+            mInRejectedExclusion = mUnrestrictedExcludeRegion.contains(x, y);
         }
         return !isInExcludedRegion;
     }
@@ -325,6 +340,7 @@
     private void cancelGesture(MotionEvent ev) {
         // Send action cancel to reset all the touch events
         mAllowGesture = false;
+        mInRejectedExclusion = false;
         MotionEvent cancelEv = MotionEvent.obtain(ev);
         cancelEv.setAction(MotionEvent.ACTION_CANCEL);
         mEdgePanel.handleTouch(cancelEv);
@@ -338,6 +354,7 @@
             // either the bouncer is showing or the notification panel is hidden
             int stateFlags = mOverviewProxyService.getSystemUiStateFlags();
             mIsOnLeftEdge = ev.getX() <= mEdgeWidth + mLeftInset;
+            mInRejectedExclusion = false;
             mAllowGesture = !QuickStepContract.isBackGestureDisabled(stateFlags)
                     && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
             if (mAllowGesture) {
@@ -392,6 +409,14 @@
                 }
                 mOverviewProxyService.notifyBackAction(performAction, (int) mDownPoint.x,
                         (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
+                int backtype = performAction ? (mInRejectedExclusion
+                        ? StatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED :
+                                StatsLog.BACK_GESTURE__TYPE__COMPLETED) :
+                                        StatsLog.BACK_GESTURE__TYPE__INCOMPLETE;
+                StatsLog.write(StatsLog.BACK_GESTURE_REPORTED_REPORTED, backtype,
+                        (int) mDownPoint.y, mIsOnLeftEdge
+                                ? StatsLog.BACK_GESTURE__X_LOCATION__LEFT :
+                                StatsLog.BACK_GESTURE__X_LOCATION__RIGHT);
             }
             if (isUp || action == MotionEvent.ACTION_CANCEL) {
                 mRegionSamplingHelper.stop();
@@ -463,7 +488,9 @@
         pw.println("EdgeBackGestureHandler:");
         pw.println("  mIsEnabled=" + mIsEnabled);
         pw.println("  mAllowGesture=" + mAllowGesture);
+        pw.println("  mInRejectedExclusion" + mInRejectedExclusion);
         pw.println("  mExcludeRegion=" + mExcludeRegion);
+        pw.println("  mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion);
         pw.println("  mImeHeight=" + mImeHeight);
         pw.println("  mIsAttached=" + mIsAttached);
         pw.println("  mEdgeWidth=" + mEdgeWidth);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
deleted file mode 100644
index 8bb8ca2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.phone;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.view.CompositionSamplingListener;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.shared.system.QuickStepContract;
-
-import java.io.PrintWriter;
-
-/**
- * Updates the nav bar tint based on the color of the content behind the nav bar.
- */
-public class NavBarTintController implements View.OnAttachStateChangeListener,
-        View.OnLayoutChangeListener {
-
-    public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
-    public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
-
-    private final Handler mHandler = new Handler();
-    private final NavigationBarView mNavigationBarView;
-    private final LightBarTransitionsController mLightBarController;
-    private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
-    private boolean mWindowVisible;
-
-    private final CompositionSamplingListener mSamplingListener;
-    private final Runnable mUpdateSamplingListener = this::updateSamplingListener;
-    private final Rect mSamplingBounds = new Rect();
-    private boolean mSamplingEnabled = false;
-    private boolean mSamplingListenerRegistered = false;
-
-    private float mLastMedianLuma;
-    private float mCurrentMedianLuma;
-    private boolean mUpdateOnNextDraw;
-
-    private final int mNavBarHeight;
-    private final int mNavColorSampleMargin;
-
-    // Passing the threshold of this luminance value will make the button black otherwise white
-    private final float mLuminanceThreshold;
-    private final float mLuminanceChangeThreshold;
-
-    public NavBarTintController(NavigationBarView navigationBarView,
-            LightBarTransitionsController lightBarController) {
-        mSamplingListener = new CompositionSamplingListener(
-                navigationBarView.getContext().getMainExecutor()) {
-            @Override
-            public void onSampleCollected(float medianLuma) {
-                updateTint(medianLuma);
-            }
-        };
-        mNavigationBarView = navigationBarView;
-        mNavigationBarView.addOnAttachStateChangeListener(this);
-        mNavigationBarView.addOnLayoutChangeListener(this);
-        mLightBarController = lightBarController;
-
-        final Resources res = navigationBarView.getResources();
-        mNavBarHeight = res.getDimensionPixelSize(R.dimen.navigation_bar_height);
-        mNavColorSampleMargin =
-                res.getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
-        mLuminanceThreshold = res.getFloat(R.dimen.navigation_luminance_threshold);
-        mLuminanceChangeThreshold = res.getFloat(R.dimen.navigation_luminance_change_threshold);
-    }
-
-    void onDraw() {
-        if (mUpdateOnNextDraw) {
-            mUpdateOnNextDraw = false;
-            requestUpdateSamplingListener();
-        }
-    }
-
-    void start() {
-        if (!isEnabled(mNavigationBarView.getContext(), mNavBarMode)) {
-            return;
-        }
-        mSamplingEnabled = true;
-        // Defer calling updateSamplingListener since we may have just reinflated prior to this
-        requestUpdateSamplingListener();
-    }
-
-    void stop() {
-        mSamplingEnabled = false;
-        requestUpdateSamplingListener();
-    }
-
-    @Override
-    public void onViewAttachedToWindow(View view) {
-        requestUpdateSamplingListener();
-    }
-
-    @Override
-    public void onViewDetachedFromWindow(View view) {
-        // Defer calling updateSamplingListener the attach info has not yet been reset
-        requestUpdateSamplingListener();
-    }
-
-    @Override
-    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-        mSamplingBounds.setEmpty();
-        // TODO: Extend this to 2/3 button layout as well
-        View view = mNavigationBarView.getHomeHandle().getCurrentView();
-        if (view != null) {
-            int[] pos = new int[2];
-            view.getLocationOnScreen(pos);
-            Point displaySize = new Point();
-            view.getContext().getDisplay().getRealSize(displaySize);
-            final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin,
-                    displaySize.y - mNavBarHeight, pos[0] + view.getWidth() + mNavColorSampleMargin,
-                    displaySize.y);
-            if (!samplingBounds.equals(mSamplingBounds)) {
-                mSamplingBounds.set(samplingBounds);
-                requestUpdateSamplingListener();
-            }
-        }
-    }
-
-    private void requestUpdateSamplingListener() {
-        mHandler.removeCallbacks(mUpdateSamplingListener);
-        mHandler.post(mUpdateSamplingListener);
-    }
-
-    private void updateSamplingListener() {
-        if (mSamplingListenerRegistered) {
-            mSamplingListenerRegistered = false;
-            CompositionSamplingListener.unregister(mSamplingListener);
-        }
-        if (mSamplingEnabled && mWindowVisible && !mSamplingBounds.isEmpty()
-                && mNavigationBarView.isAttachedToWindow()) {
-            if (!mNavigationBarView.getViewRootImpl().getSurfaceControl().isValid()) {
-                // The view may still be attached, but the surface backing the window can be
-                // destroyed, so wait until the next draw to update the listener again
-                mUpdateOnNextDraw = true;
-                return;
-            }
-            mSamplingListenerRegistered = true;
-            CompositionSamplingListener.register(mSamplingListener, DEFAULT_DISPLAY,
-                    mNavigationBarView.getViewRootImpl().getSurfaceControl(),
-                    mSamplingBounds);
-        }
-    }
-
-    private void updateTint(float medianLuma) {
-        mLastMedianLuma = medianLuma;
-
-        // If the difference between the new luma and the current luma is larger than threshold
-        // then apply the current luma, this is to prevent small changes causing colors to flicker
-        if (Math.abs(mCurrentMedianLuma - mLastMedianLuma) > mLuminanceChangeThreshold) {
-            if (medianLuma > mLuminanceThreshold) {
-                // Black
-                mLightBarController.setIconsDark(true /* dark */, true /* animate */);
-            } else {
-                // White
-                mLightBarController.setIconsDark(false /* dark */, true /* animate */);
-            }
-            mCurrentMedianLuma = medianLuma;
-        }
-    }
-
-    public void setWindowVisible(boolean visible) {
-        mWindowVisible = visible;
-        requestUpdateSamplingListener();
-    }
-
-    public void onNavigationModeChanged(int mode) {
-        mNavBarMode = mode;
-    }
-
-    void dump(PrintWriter pw) {
-        pw.println("NavBarTintController:");
-        pw.println("  navBar isAttached: " + mNavigationBarView.isAttachedToWindow());
-        pw.println("  navBar isScValid: " + (mNavigationBarView.isAttachedToWindow()
-                ? mNavigationBarView.getViewRootImpl().getSurfaceControl().isValid()
-                : "false"));
-        pw.println("  mSamplingListenerRegistered: " + mSamplingListenerRegistered);
-        pw.println("  mSamplingBounds: " + mSamplingBounds);
-        pw.println("  mLastMedianLuma: " + mLastMedianLuma);
-        pw.println("  mCurrentMedianLuma: " + mCurrentMedianLuma);
-        pw.println("  mWindowVisible: " + mWindowVisible);
-    }
-
-    public static boolean isEnabled(Context context, int navBarMode) {
-        return context.getDisplayId() == DEFAULT_DISPLAY
-                && QuickStepContract.isGesturalMode(navBarMode);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index e9731c5..5d3f3ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -1063,16 +1063,7 @@
             if (Intent.ACTION_SCREEN_OFF.equals(action)
                     || Intent.ACTION_SCREEN_ON.equals(action)) {
                 notifyNavigationBarScreenOn();
-
-                if (Intent.ACTION_SCREEN_ON.equals(action)) {
-                    // Enabled and screen is on, start it again if enabled
-                    if (NavBarTintController.isEnabled(getContext(), mNavBarMode)) {
-                        mNavigationBarView.getTintController().start();
-                    }
-                } else {
-                    // Screen off disable it
-                    mNavigationBarView.getTintController().stop();
-                }
+                mNavigationBarView.onScreenStateChanged(Intent.ACTION_SCREEN_ON.equals(action));
             }
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                 // The accessibility settings may be different for the new user
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index 23cc0fc..3b59031 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -18,8 +18,7 @@
 
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 
-import static com.android.systemui.statusbar.phone.NavBarTintController.DEFAULT_COLOR_ADAPT_TRANSITION_TIME;
-import static com.android.systemui.statusbar.phone.NavBarTintController.MIN_COLOR_ADAPT_TRANSITION_TIME;
+import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -42,6 +41,9 @@
 public final class NavigationBarTransitions extends BarTransitions implements
         LightBarTransitionsController.DarkIntensityApplier {
 
+    public static final int MIN_COLOR_ADAPT_TRANSITION_TIME = 400;
+    public static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700;
+
     /**
      * Notified when the color of nav bar elements changes.
      */
@@ -124,7 +126,7 @@
     @Override
     public void setAutoDim(boolean autoDim) {
         // Ensure we aren't in gestural nav if we are triggering auto dim
-        if (autoDim && NavBarTintController.isEnabled(mView.getContext(), mNavBarMode)) return;
+        if (autoDim && isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) return;
         if (mAutoDim == autoDim) return;
         mAutoDim = autoDim;
         applyLightsOut(true, false);
@@ -201,7 +203,7 @@
 
     @Override
     public int getTintAnimationDuration() {
-        if (NavBarTintController.isEnabled(mView.getContext(), mNavBarMode)) {
+        if (isGesturalModeOnDefaultDisplay(mView.getContext(), mNavBarMode)) {
             return Math.max(DEFAULT_COLOR_ADAPT_TRANSITION_TIME, MIN_COLOR_ADAPT_TRANSITION_TIME);
         }
         return LightBarTransitionsController.DEFAULT_TINT_ANIMATION_DURATION;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index f689a3e..3485f23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -26,6 +26,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SEARCH_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
+import static com.android.systemui.util.Utils.isGesturalModeOnDefaultDisplay;
 
 import android.animation.LayoutTransition;
 import android.animation.LayoutTransition.TransitionListener;
@@ -89,6 +90,8 @@
     final static boolean SLIPPERY_WHEN_DISABLED = true;
 
     final static boolean ALTERNATE_CAR_MODE_UI = false;
+    private final RegionSamplingHelper mRegionSamplingHelper;
+    private final int mNavColorSampleMargin;
 
     View mCurrentView = null;
     private View mVertical;
@@ -101,7 +104,7 @@
     boolean mLongClickableAccessibilityButton;
     int mDisabledFlags = 0;
     int mNavigationIconHints = 0;
-    private int mNavBarMode = NAV_BAR_MODE_3BUTTON;
+    private int mNavBarMode;
 
     private Rect mHomeButtonBounds = new Rect();
     private Rect mBackButtonBounds = new Rect();
@@ -143,14 +146,13 @@
     private FloatingRotationButton mFloatingRotationButton;
     private RotationButtonController mRotationButtonController;
 
-    private NavBarTintController mTintController;
-
     /**
      * Helper that is responsible for showing the right toast when a disallowed activity operation
      * occurred. In pinned mode, we show instructions on how to break out of this mode, whilst in
      * fully locked mode we only show that unlocking is blocked.
      */
     private ScreenPinningNotify mScreenPinningNotify;
+    private Rect mSamplingBounds = new Rect();
 
     private class NavTransitionListener implements TransitionListener {
         private boolean mBackTransitioning;
@@ -305,12 +307,30 @@
         mButtonDispatchers.put(R.id.menu_container, mContextualButtonGroup);
         mDeadZone = new DeadZone(this);
 
-        mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
-        mTintController = new NavBarTintController(this, getLightTransitionsController());
-    }
+        mNavColorSampleMargin =
+                getResources()
+                        .getDimensionPixelSize(R.dimen.navigation_handle_sample_horizontal_margin);
 
-    public NavBarTintController getTintController() {
-        return mTintController;
+        mEdgeBackGestureHandler = new EdgeBackGestureHandler(context, mOverviewProxyService);
+        mRegionSamplingHelper = new RegionSamplingHelper(this,
+                new RegionSamplingHelper.SamplingCallback() {
+                    @Override
+                    public void onRegionDarknessChanged(boolean isRegionDark) {
+                        getLightTransitionsController().setIconsDark(!isRegionDark ,
+                                true /* animate */);
+                    }
+
+                    @Override
+                    public Rect getSampledRegion(View sampledView) {
+                        updateSamplingRect();
+                        return mSamplingBounds;
+                    }
+
+                    @Override
+                    public boolean isSamplingEnabled() {
+                        return isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode);
+                    }
+                });
     }
 
     public NavigationBarTransitions getBarTransitions() {
@@ -326,12 +346,6 @@
         updatePanelSystemUiStateFlags();
     }
 
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-        mTintController.onDraw();
-    }
-
     public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) {
         mOnVerticalChangedListener = onVerticalChangedListener;
         notifyVerticalChangedListener(mIsVertical);
@@ -352,10 +366,10 @@
         if (newMode == MODE_OPAQUE) {
             // If the nav bar background is opaque, stop auto tinting since we know the icons are
             // showing over a dark background
-            mTintController.stop();
+            mRegionSamplingHelper.stop();
             getLightTransitionsController().setIconsDark(false /* dark */, true /* animate */);
         } else {
-            mTintController.start();
+            mRegionSamplingHelper.start(mSamplingBounds);
         }
     }
 
@@ -535,8 +549,19 @@
         return KeyButtonDrawable.create(mContext, icon, hasShadow);
     }
 
+    /** To be called when screen lock/unlock state changes */
+    public void onScreenStateChanged(boolean isScreenOn) {
+        if (isScreenOn) {
+            if (isGesturalModeOnDefaultDisplay(getContext(), mNavBarMode)) {
+                mRegionSamplingHelper.start(mSamplingBounds);
+            }
+        } else {
+            mRegionSamplingHelper.stop();
+        }
+    }
+
     public void setWindowVisible(boolean visible) {
-        mTintController.setWindowVisible(visible);
+        mRegionSamplingHelper.setWindowVisible(visible);
         mRotationButtonController.onNavigationBarWindowVisibilityChange(visible);
     }
 
@@ -799,13 +824,7 @@
         mRecentsOnboarding.onNavigationModeChanged(mNavBarMode);
         getRotateSuggestionButton().onNavigationModeChanged(mNavBarMode);
 
-        // Color adaption is tied with showing home handle, only avaliable if visible
-        mTintController.onNavigationModeChanged(mNavBarMode);
-        if (isGesturalMode(mNavBarMode)) {
-            mTintController.start();
-        } else {
-            mTintController.stop();
-        }
+        mRegionSamplingHelper.start(mSamplingBounds);
     }
 
     public void setAccessibilityButtonState(final boolean visible, final boolean longClickable) {
@@ -836,6 +855,24 @@
         super.onDraw(canvas);
     }
 
+    private void updateSamplingRect() {
+        mSamplingBounds.setEmpty();
+        // TODO: Extend this to 2/3 button layout as well
+        View view = getHomeHandle().getCurrentView();
+
+        if (view != null) {
+            int[] pos = new int[2];
+            view.getLocationOnScreen(pos);
+            Point displaySize = new Point();
+            view.getContext().getDisplay().getRealSize(displaySize);
+            final Rect samplingBounds = new Rect(pos[0] - mNavColorSampleMargin,
+                    displaySize.y - getNavBarHeight(),
+                    pos[0] + view.getWidth() + mNavColorSampleMargin,
+                    displaySize.y);
+            mSamplingBounds.set(samplingBounds);
+        }
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -920,7 +957,8 @@
     }
 
     public void showPinningEscapeToast() {
-        mScreenPinningNotify.showEscapeToast(isRecentsButtonVisible());
+        mScreenPinningNotify.showEscapeToast(
+                mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible());
     }
 
     public boolean isVertical() {
@@ -984,6 +1022,14 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
+    private int getNavBarHeight() {
+        return mIsVertical
+                ? getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height_landscape)
+                : getResources().getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_height);
+    }
+
     private void notifyVerticalChangedListener(boolean newVertical) {
         if (mOnVerticalChangedListener != null) {
             mOnVerticalChangedListener.onVerticalChanged(newVertical);
@@ -1130,7 +1176,7 @@
 
         mContextualButtonGroup.dump(pw);
         mRecentsOnboarding.dump(pw);
-        mTintController.dump(pw);
+        mRegionSamplingHelper.dump(pw);
         mEdgeBackGestureHandler.dump(pw);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
index 9988c85..5d8044f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationPrototypeController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.annotation.IntDef;
 import android.content.ComponentCallbacks;
@@ -29,8 +29,6 @@
 import android.os.Handler;
 import android.provider.Settings;
 
-import com.android.systemui.shared.system.QuickStepContract;
-
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -120,8 +118,7 @@
             } else if (path.endsWith(HIDE_HOME_BUTTON_SETTING)) {
                 mListener.onHomeButtonVisibilityChanged(!hideHomeButton());
             } else if (path.endsWith(NAV_COLOR_ADAPT_ENABLE_SETTING)) {
-                mListener.onColorAdaptChanged(
-                        NavBarTintController.isEnabled(mContext, NAV_BAR_MODE_GESTURAL));
+                mListener.onColorAdaptChanged(mContext.getDisplayId() == DEFAULT_DISPLAY);
             } else if (path.endsWith(SHOW_HOME_HANDLE_SETTING)) {
                 mListener.onHomeHandleVisiblilityChanged(showHomeHandle());
             } else if (path.endsWith(ENABLE_ASSISTANT_GESTURE)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
index 8026f65..c1ff572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/RegionSamplingHelper.java
@@ -29,6 +29,8 @@
 
 import com.android.systemui.R;
 
+import java.io.PrintWriter;
+
 /**
  * A helper class to sample regions on the screen and inspect its luminosity.
  */
@@ -62,6 +64,7 @@
     private final float mLuminanceThreshold;
     private final float mLuminanceChangeThreshold;
     private boolean mFirstSamplingAfterStart;
+    private boolean mWindowVisible;
     private SurfaceControl mRegisteredStopLayer = null;
     private ViewTreeObserver.OnDrawListener mUpdateOnDraw = new ViewTreeObserver.OnDrawListener() {
         @Override
@@ -148,7 +151,9 @@
     }
 
     private void updateSamplingListener() {
-        boolean isSamplingEnabled = mSamplingEnabled && !mSamplingRequestBounds.isEmpty()
+        boolean isSamplingEnabled = mSamplingEnabled
+                && !mSamplingRequestBounds.isEmpty()
+                && mWindowVisible
                 && (mSampledView.isAttachedToWindow() || mFirstSamplingAfterStart);
         if (isSamplingEnabled) {
             ViewRootImpl viewRootImpl = mSampledView.getViewRootImpl();
@@ -216,6 +221,24 @@
         }
     }
 
+    void setWindowVisible(boolean visible) {
+        mWindowVisible = visible;
+        updateSamplingListener();
+    }
+
+    void dump(PrintWriter pw) {
+        pw.println("RegionSamplingHelper:");
+        pw.println("  sampleView isAttached: " + mSampledView.isAttachedToWindow());
+        pw.println("  sampleView isScValid: " + (mSampledView.isAttachedToWindow()
+                ? mSampledView.getViewRootImpl().getSurfaceControl().isValid()
+                : "false"));
+        pw.println("  mSamplingListenerRegistered: " + mSamplingListenerRegistered);
+        pw.println("  mSamplingRequestBounds: " + mSamplingRequestBounds);
+        pw.println("  mLastMedianLuma: " + mLastMedianLuma);
+        pw.println("  mCurrentMedianLuma: " + mCurrentMedianLuma);
+        pw.println("  mWindowVisible: " + mWindowVisible);
+    }
+
     public interface SamplingCallback {
         /**
          * Called when the darkness of the sampled region changes
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
index f8731b4..071e00d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
@@ -51,7 +51,7 @@
     }
 
     /** Show a toast that describes the gesture the user should use to escape pinned mode. */
-    public void showEscapeToast(boolean isRecentsButtonVisible) {
+    public void showEscapeToast(boolean isGestureNavEnabled, boolean isRecentsButtonVisible) {
         long showToastTime = SystemClock.elapsedRealtime();
         if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
             Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -60,9 +60,11 @@
         if (mLastToast != null) {
             mLastToast.cancel();
         }
-        mLastToast = makeAllUserToastAndShow(isRecentsButtonVisible
-                ? R.string.screen_pinning_toast
-                : R.string.screen_pinning_toast_recents_invisible);
+        mLastToast = makeAllUserToastAndShow(isGestureNavEnabled
+                ? R.string.screen_pinning_toast_gesture_nav
+                : isRecentsButtonVisible
+                        ? R.string.screen_pinning_toast
+                        : R.string.screen_pinning_toast_recents_invisible);
         mLastShowToastTime = showToastTime;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index b12bf5c..a7262cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -47,6 +47,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.util.AlarmTimeout;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -177,7 +178,7 @@
     public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
             TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
             Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
-            AlarmManager alarmManager) {
+            AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
         mScrimStateListener = scrimStateListener;
@@ -197,6 +198,13 @@
         // to make sure that text on top of it is legible.
         mScrimBehindAlpha = mScrimBehindAlphaResValue;
         mDozeParameters = dozeParameters;
+        keyguardMonitor.addCallback(new KeyguardMonitor.Callback() {
+            @Override
+            public void onKeyguardFadingAwayChanged() {
+                setKeyguardFadingAway(keyguardMonitor.isKeyguardFadingAway(),
+                        keyguardMonitor.getKeyguardFadingAwayDuration());
+            }
+        });
 
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mColorExtractor.addOnColorsChangedListener(this);
@@ -948,9 +956,9 @@
         }
     }
 
-    public void setUnlockIsFading(boolean unlockFading) {
+    private void setKeyguardFadingAway(boolean fadingAway, long duration) {
         for (ScrimState state : ScrimState.values()) {
-            state.setUnlockIsFading(unlockFading);
+            state.setKeyguardFadingAway(fadingAway, duration);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index b45914b..9fdd3b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -156,8 +156,8 @@
         public void prepare(ScrimState previousState) {
             mCurrentBehindAlpha = 0;
             mCurrentInFrontAlpha = 0;
-            mAnimationDuration = mUnlockIsFading
-                    ? KeyguardBypassController.BYPASS_PANEL_FADE_DURATION
+            mAnimationDuration = mKeyguardFadingAway
+                    ? mKeyguardFadingAwayDuration
                     : StatusBar.FADE_KEYGUARD_DURATION;
             mAnimateChange = !mLaunchingAffordanceWithPreview;
 
@@ -209,7 +209,8 @@
     boolean mHasBackdrop;
     boolean mLaunchingAffordanceWithPreview;
     boolean mWakeLockScreenSensorActive;
-    boolean mUnlockIsFading;
+    boolean mKeyguardFadingAway;
+    long mKeyguardFadingAwayDuration;
 
     ScrimState(int index) {
         mIndex = index;
@@ -298,7 +299,8 @@
         mWakeLockScreenSensorActive = active;
     }
 
-    public void setUnlockIsFading(boolean unlockIsFading) {
-        mUnlockIsFading = unlockIsFading;
+    public void setKeyguardFadingAway(boolean fadingAway, long duration) {
+        mKeyguardFadingAway = fadingAway;
+        mKeyguardFadingAwayDuration = duration;
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 90aba87..f158ca1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -948,7 +948,8 @@
                         mStatusBarWindow.onScrimVisibilityChanged(scrimsVisible);
                     }
                 }, DozeParameters.getInstance(mContext),
-                mContext.getSystemService(AlarmManager.class));
+                mContext.getSystemService(AlarmManager.class),
+                mKeyguardMonitor);
         mNotificationPanel.initDependencies(this, mGroupManager, mNotificationShelf,
                 mHeadsUpManager, mNotificationIconAreaController, mScrimController);
         mDozeScrimController = new DozeScrimController(DozeParameters.getInstance(context));
@@ -3874,7 +3875,6 @@
 
     public void notifyBiometricAuthModeChanged() {
         updateDozing();
-        mScrimController.setUnlockIsFading(mBiometricUnlockController.isUnlockFading());
         updateScrimController();
         mStatusBarWindow.onBiometricAuthModeChanged(mBiometricUnlockController.isWakeAndUnlock(),
                 mBiometricUnlockController.isBiometricUnlock());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 830b50e..8b06a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -20,6 +20,8 @@
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 
 public interface HotspotController extends CallbackController<Callback>, Dumpable {
+    void handleSetListening(boolean listening);
+
     boolean isHotspotEnabled();
     boolean isHotspotTransient();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index db2be0e..1c6d12f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -51,6 +51,7 @@
     private int mHotspotState;
     private int mNumConnectedDevices;
     private boolean mWaitingForTerminalState;
+    private boolean mListening;
 
     /**
      */
@@ -105,14 +106,18 @@
             if (DEBUG) Log.d(TAG, "addCallback " + callback);
             mCallbacks.add(callback);
             if (mWifiManager != null) {
-                if (mCallbacks.size() == 1) {
-                    mWifiManager.registerSoftApCallback(this, mMainHandler);
-                } else {
-                    // mWifiManager#registerSoftApCallback triggers a call to onNumClientsChanged
-                    // on the Main Handler. In order to always update the callback on added, we
-                    // make this call when adding callbacks after the first.
-                    mMainHandler.post(() ->
-                            callback.onHotspotChanged(isHotspotEnabled(), mNumConnectedDevices));
+                if (mListening) {
+                    if (mCallbacks.size() == 1) {
+                        mWifiManager.registerSoftApCallback(this, mMainHandler);
+                    } else {
+                        // mWifiManager#registerSoftApCallback triggers a call to
+                        // onNumClientsChanged on the Main Handler. In order to always update the
+                        // callback on added, we make this call when adding callbacks after the
+                        // first.
+                        mMainHandler.post(() ->
+                                callback.onHotspotChanged(isHotspotEnabled(),
+                                        mNumConnectedDevices));
+                    }
                 }
             }
         }
@@ -124,13 +129,24 @@
         if (DEBUG) Log.d(TAG, "removeCallback " + callback);
         synchronized (mCallbacks) {
             mCallbacks.remove(callback);
-            if (mCallbacks.isEmpty() && mWifiManager != null) {
+            if (mCallbacks.isEmpty() && mWifiManager != null && mListening) {
                 mWifiManager.unregisterSoftApCallback(this);
             }
         }
     }
 
     @Override
+    public void handleSetListening(boolean listening) {
+        // Wait for the first |handleSetListening(true))| to register softap callbacks (for lazy
+        // registration of the softap callbacks).
+        if (mListening || !listening) return;
+        mListening = true;
+        if (mCallbacks.size() >= 1) {
+            mWifiManager.registerSoftApCallback(this, mMainHandler);
+        }
+    }
+
+    @Override
     public boolean isHotspotEnabled() {
         return mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index 070136e..e1ef809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -76,7 +76,7 @@
     }
 
     interface Callback {
-        void onKeyguardShowingChanged();
+        default void onKeyguardShowingChanged() {}
         default void onKeyguardFadingAwayChanged() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index 8829be4..87ed14a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -142,10 +142,10 @@
     }
 
     public void notifyKeyguardFadingAway(long delay, long fadeoutDuration, boolean isBypassFading) {
-        setKeyguardFadingAway(true);
         mKeyguardFadingAwayDelay = delay;
         mKeyguardFadingAwayDuration = fadeoutDuration;
         mBypassFadingAnimation = isBypassFading;
+        setKeyguardFadingAway(true);
     }
 
     private void setKeyguardFadingAway(boolean keyguardFadingAway) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 4562763..e75365e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -216,10 +216,14 @@
 
         MobileIconGroup hGroup = TelephonyIcons.THREE_G;
         MobileIconGroup hPlusGroup = TelephonyIcons.THREE_G;
-        if (mConfig.hspaDataDistinguishable) {
+        if (mConfig.show4gFor3g) {
+            hGroup = TelephonyIcons.FOUR_G;
+            hPlusGroup = TelephonyIcons.FOUR_G;
+        } else if (mConfig.hspaDataDistinguishable) {
             hGroup = TelephonyIcons.H;
             hPlusGroup = TelephonyIcons.H_PLUS;
         }
+
         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
         mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index d545dc8..04f96a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -312,6 +312,7 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        filter.addAction(Intent.ACTION_BOOT_COMPLETED);
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         mContext.registerReceiver(this, filter, null, mReceiverHandler);
         mListening = true;
@@ -513,6 +514,9 @@
                     recalculateEmergency();
                 }
                 break;
+            case Intent.ACTION_BOOT_COMPLETED:
+                mWifiSignalController.handleBootCompleted();
+                break;
             case CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED:
                 mConfig = Config.readConfig(mContext);
                 mReceiverHandler.post(this::handleConfigurationChanged);
@@ -1114,6 +1118,7 @@
         Map<Integer, MobileIconGroup> nr5GIconMap = new HashMap<>();
 
         boolean showAtLeast3G = false;
+        boolean show4gFor3g = false;
         boolean alwaysShowCdmaRssi = false;
         boolean show4gForLte = false;
         boolean hideLtePlus = false;
@@ -1158,6 +1163,8 @@
                         CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
                 config.show4gForLte = b.getBoolean(
                         CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
+                config.show4gFor3g = b.getBoolean(
+                        CarrierConfigManager.KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL);
                 config.hideLtePlus = b.getBoolean(
                         CarrierConfigManager.KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL);
                 config.patternOfCarrierSpecificDataIcon = b.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 6f63544..a441f66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -38,6 +38,7 @@
 public class WifiSignalController extends
         SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
     private final boolean mHasMobileData;
+    private final WifiManager mWifiManager;
     private final WifiStatusTracker mWifiTracker;
 
     public WifiSignalController(Context context, boolean hasMobileData,
@@ -49,13 +50,11 @@
                 context.getSystemService(NetworkScoreManager.class);
         ConnectivityManager connectivityManager =
                 context.getSystemService(ConnectivityManager.class);
+        mWifiManager = wifiManager;
         mWifiTracker = new WifiStatusTracker(mContext, wifiManager, networkScoreManager,
                 connectivityManager, this::handleStatusUpdated);
         mWifiTracker.setListening(true);
         mHasMobileData = hasMobileData;
-        if (wifiManager != null) {
-            wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(), null);
-        }
         // WiFi only has one state.
         mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
                 "Wi-Fi Icons",
@@ -128,6 +127,10 @@
         notifyListenersIfNecessary();
     }
 
+    public void handleBootCompleted() {
+        mWifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(), null);
+    }
+
     /**
      * Handler to receive the data activity on wifi.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 1102bb7..92a8d84 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -14,12 +14,16 @@
 
 package com.android.systemui.util;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.Manifest;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.view.View;
 
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.CommandQueue;
 
 import java.util.List;
@@ -110,4 +114,14 @@
         return pm.queryIntentActivities(homeIntent, 0).isEmpty();
     }
 
+    /**
+     * Returns {@code true} if the navMode is that of
+     * {@link android.view.WindowManagerPolicyConstants#NAV_BAR_MODE_GESTURAL} AND
+     * the context is that of the default display
+     */
+    public static boolean isGesturalModeOnDefaultDisplay(Context context, int navMode) {
+        return context.getDisplayId() == DEFAULT_DISPLAY
+                && QuickStepContract.isGesturalMode(navMode);
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java
new file mode 100644
index 0000000..8f2f8b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2019 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.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogImplTest extends SysuiTestCase {
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private IBiometricServiceReceiverInternal mReceiver;
+    @Mock
+    private BiometricDialog mDialog1;
+    @Mock
+    private BiometricDialog mDialog2;
+
+    private TestableBiometricDialogImpl mBiometricDialogImpl;
+
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+
+        TestableContext context = spy(mContext);
+
+        mContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+        when(context.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
+            .thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+            .thenReturn(true);
+
+        when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
+        when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
+
+        mBiometricDialogImpl = new TestableBiometricDialogImpl(new MockInjector());
+        mBiometricDialogImpl.mContext = context;
+        mBiometricDialogImpl.mComponents = mContext.getComponents();
+
+        mBiometricDialogImpl.start();
+    }
+
+    // Callback tests
+
+    @Test
+    public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_USER_CANCELED);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+    }
+
+    @Test
+    public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BUTTON_NEGATIVE);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+    }
+
+    @Test
+    public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BUTTON_POSITIVE);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+    }
+
+    @Test
+    public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_AUTHENTICATED);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+    }
+
+    @Test
+    public void testSendsReasonError_whenDismissedByError() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_ERROR);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+    }
+
+    @Test
+    public void testSendsReasonDismissedBySystemServer_whenDismissedByServer() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+    }
+
+    // Statusbar tests
+
+    @Test
+    public void testShowInvoked_whenSystemRequested()
+            throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+    }
+
+    @Test
+    public void testOnAuthenticationSucceededInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.onBiometricAuthenticated(true, null /* failureReason */);
+        verify(mDialog1).onAuthenticationSucceeded();
+    }
+
+    @Test
+    public void testOnAuthenticationFailedInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        final String failureReason = "failure reason";
+        mBiometricDialogImpl.onBiometricAuthenticated(false, failureReason);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onAuthenticationFailed(captor.capture());
+
+        assertEquals(captor.getValue(), failureReason);
+    }
+
+    @Test
+    public void testOnHelpInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        final String helpMessage = "help";
+        mBiometricDialogImpl.onBiometricHelp(helpMessage);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onHelp(captor.capture());
+
+        assertEquals(captor.getValue(), helpMessage);
+    }
+
+    @Test
+    public void testOnErrorInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        final String errMessage = "error message";
+        mBiometricDialogImpl.onBiometricError(errMessage);
+
+        ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+        verify(mDialog1).onError(captor.capture());
+
+        assertEquals(captor.getValue(), errMessage);
+    }
+
+    @Test
+    public void testDismissWithoutCallbackInvoked_whenSystemRequested() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.hideBiometricDialog();
+        verify(mDialog1).dismissFromSystemServer();
+    }
+
+    @Test
+    public void testClientNotified_whenDismissedBySystemServer() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        mBiometricDialogImpl.hideBiometricDialog();
+        verify(mDialog1).dismissFromSystemServer();
+
+        assertNotNull(mBiometricDialogImpl.mCurrentDialog);
+        assertNotNull(mBiometricDialogImpl.mReceiver);
+    }
+
+    // Corner case tests
+
+    @Test
+    public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+
+        showDialog(BiometricPrompt.TYPE_FACE);
+
+        // First dialog should be dismissed without animation
+        verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
+
+        // Second dialog should be shown without animation
+        verify(mDialog2).show(any(), eq(true)) /* skipIntro */;
+    }
+
+    @Test
+    public void testConfigurationPersists_whenOnConfigurationChanged() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+        verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+
+        mBiometricDialogImpl.onConfigurationChanged(new Configuration());
+
+        ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
+        verify(mDialog1).onSaveState(captor.capture());
+
+        // Old dialog doesn't animate
+        verify(mDialog1).dismissWithoutCallback(eq(false /* animate */));
+
+        // Saved state is restored into new dialog
+        ArgumentCaptor<Bundle> captor2 = ArgumentCaptor.forClass(Bundle.class);
+        verify(mDialog2).restoreState(captor2.capture());
+
+        // Dialog for new configuration skips intro
+        verify(mDialog2).show(any(), eq(true) /* skipIntro */);
+
+        // TODO: This should check all values we want to save/restore
+        assertEquals(captor.getValue(), captor2.getValue());
+    }
+
+    @Test
+    public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
+        showDialog(BiometricPrompt.TYPE_FACE);
+
+        List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
+        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+        taskInfo.topActivity = mock(ComponentName.class);
+        when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
+        tasks.add(taskInfo);
+        when(mBiometricDialogImpl.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
+
+        mBiometricDialogImpl.mTaskStackListener.onTaskStackChanged();
+        waitForIdleSync();
+
+        assertNull(mBiometricDialogImpl.mCurrentDialog);
+        assertNull(mBiometricDialogImpl.mReceiver);
+        verify(mDialog1).dismissWithoutCallback(true /* animate */);
+        verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
+    }
+
+    // Helpers
+
+    private void showDialog(int type) {
+        mBiometricDialogImpl.showBiometricDialog(createTestDialogBundle(),
+                mReceiver /* receiver */,
+                type,
+                true /* requireConfirmation */,
+                0 /* userId */,
+                "testPackage");
+    }
+
+    private Bundle createTestDialogBundle() {
+        Bundle bundle = new Bundle();
+
+        bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
+        bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, "Subtitle");
+        bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, "Description");
+        bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative Button");
+
+        // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+        // by user settings, and should be tested in BiometricService.
+        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+        return bundle;
+    }
+
+    private final class TestableBiometricDialogImpl extends BiometricDialogImpl {
+        private int mBuildCount = 0;
+
+        public TestableBiometricDialogImpl(Injector injector) {
+            super(injector);
+        }
+
+        @Override
+        protected BiometricDialog buildDialog(Bundle biometricPromptBundle,
+                boolean requireConfirmation, int userId, int type, String opPackageName) {
+            BiometricDialog dialog;
+            if (mBuildCount == 0) {
+                dialog = mDialog1;
+            } else if (mBuildCount == 1) {
+                dialog = mDialog2;
+            } else {
+                dialog = null;
+            }
+            mBuildCount++;
+            return dialog;
+        }
+    }
+
+    private final class MockInjector extends BiometricDialogImpl.Injector {
+        @Override
+        IActivityTaskManager getActivityTaskManager() {
+            return mock(IActivityTaskManager.class);
+        }
+    }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
new file mode 100644
index 0000000..bbdd837
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2019 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.biometrics.ui;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.spy;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogViewTest extends SysuiTestCase {
+
+    FaceDialogView mFaceDialogView;
+
+    private static final String TITLE = "Title";
+    private static final String SUBTITLE = "Subtitle";
+    private static final String DESCRIPTION = "Description";
+    private static final String NEGATIVE_BUTTON = "Negative Button";
+
+    private static final String TEST_HELP = "Help";
+
+    TestableContext mTestableContext;
+    @Mock
+    private DialogViewCallback mCallback;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private DevicePolicyManager mDpm;
+
+    private static class Injector extends BiometricDialogView.Injector {
+        @Override
+        public WakefulnessLifecycle getWakefulnessLifecycle() {
+            final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle();
+            lifecycle.dispatchFinishedWakingUp();
+            return lifecycle;
+        }
+    }
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTestableContext = spy(mContext);
+        mTestableContext.addMockSystemService(UserManager.class, mUserManager);
+        mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm);
+    }
+
+    @Test
+    public void testContentStates_confirmationRequired_authenticated() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                true /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+
+        // When starting authentication
+        assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+        assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+        assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+
+        // Contents are as expected
+        assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText()));
+        assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText()));
+        assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText()));
+        assertTrue(mFaceDialogView.mPositiveButton.getText().toString()
+                .contentEquals(mContext.getString(R.string.biometric_dialog_confirm)));
+        assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText()));
+        assertTrue(mFaceDialogView.mTryAgainButton.getText().toString()
+                .contentEquals(mContext.getString(R.string.biometric_dialog_try_again)));
+
+        // When help message is received
+        mFaceDialogView.onHelp(TEST_HELP);
+        assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+        assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+
+        // When authenticated, confirm button comes out
+        mFaceDialogView.onAuthenticationSucceeded();
+        assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+        assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled());
+    }
+
+    @Test
+    public void testContentStates_confirmationNotRequired_authenticated() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+        mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL);
+
+        assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility());
+        assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+        assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+        assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+        assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+    }
+
+    @Test
+    public void testContentStates_confirmationNotRequired_help() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+
+        mFaceDialogView.onHelp(TEST_HELP);
+        assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+        assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+    }
+
+    @Test
+    public void testBack_sendsUserCanceled() {
+        // TODO: Need robolectric framework to wait for handler to complete
+    }
+
+    @Test
+    public void testScreenOff_sendsUserCanceled() {
+        // TODO: Need robolectric framework to wait for handler to complete
+    }
+
+    @Test
+    public void testRestoreState_contentStatesCorrect() {
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.onAttachedToWindow();
+        mFaceDialogView.onAuthenticationFailed(TEST_HELP);
+
+        final Bundle bundle = new Bundle();
+        mFaceDialogView.onSaveState(bundle);
+
+        mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+                false /* requireConfirmation */);
+        mFaceDialogView.restoreState(bundle);
+        mFaceDialogView.onAttachedToWindow();
+
+        assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility());
+    }
+
+    private FaceDialogView buildFaceDialogView(Context context, DialogViewCallback callback,
+            boolean requireConfirmation) {
+        return (FaceDialogView) new BiometricDialogView.Builder(context)
+                .setCallback(callback)
+                .setBiometricPromptBundle(createTestDialogBundle())
+                .setRequireConfirmation(requireConfirmation)
+                .setUserId(0)
+                .setOpPackageName("test_package")
+                .build(BiometricDialogView.Builder.TYPE_FACE, new Injector());
+    }
+
+    private Bundle createTestDialogBundle() {
+        Bundle bundle = new Bundle();
+
+        bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE);
+        bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE);
+        bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION);
+        bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON);
+
+        // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+        // by user settings, and should be tested in BiometricService.
+        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+        return bundle;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
new file mode 100644
index 0000000..2bff548
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2019 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.broadcast
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.IntentFilter
+import android.os.Handler
+import android.os.Looper
+import android.os.UserHandle
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertSame
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+@SmallTest
+class BroadcastDispatcherTest : SysuiTestCase() {
+
+    companion object {
+        val user0 = UserHandle.of(0)
+        val user1 = UserHandle.of(1)
+
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+    }
+
+    @Mock
+    private lateinit var mockContext: Context
+    @Mock
+    private lateinit var mockUBRUser0: UserBroadcastDispatcher
+    @Mock
+    private lateinit var mockUBRUser1: UserBroadcastDispatcher
+    @Mock
+    private lateinit var broadcastReceiver: BroadcastReceiver
+    @Mock
+    private lateinit var broadcastReceiverOther: BroadcastReceiver
+    @Mock
+    private lateinit var intentFilter: IntentFilter
+    @Mock
+    private lateinit var intentFilterOther: IntentFilter
+    @Mock
+    private lateinit var mockHandler: Handler
+
+    @Captor
+    private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData>
+
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var broadcastDispatcher: BroadcastDispatcher
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+
+        broadcastDispatcher = TestBroadcastDispatcher(
+                mockContext,
+                Handler(testableLooper.looper),
+                testableLooper.looper,
+                mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
+    }
+
+    @Test
+    fun testAddingReceiverToCorrectUBR() {
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
+        broadcastDispatcher.registerReceiver(
+                broadcastReceiverOther, intentFilterOther, mockHandler, user1)
+
+        testableLooper.processAllMessages()
+
+        verify(mockUBRUser0).registerReceiver(capture(argumentCaptor))
+
+        assertSame(broadcastReceiver, argumentCaptor.value.receiver)
+        assertSame(intentFilter, argumentCaptor.value.filter)
+
+        verify(mockUBRUser1).registerReceiver(capture(argumentCaptor))
+        assertSame(broadcastReceiverOther, argumentCaptor.value.receiver)
+        assertSame(intentFilterOther, argumentCaptor.value.filter)
+    }
+
+    @Test
+    fun testRemovingReceiversRemovesFromAllUBR() {
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1)
+
+        broadcastDispatcher.unregisterReceiver(broadcastReceiver)
+
+        testableLooper.processAllMessages()
+
+        verify(mockUBRUser0).unregisterReceiver(broadcastReceiver)
+        verify(mockUBRUser1).unregisterReceiver(broadcastReceiver)
+    }
+
+    @Test
+    fun testRemoveReceiverFromUser() {
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user0)
+        broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mockHandler, user1)
+
+        broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user0)
+
+        testableLooper.processAllMessages()
+
+        verify(mockUBRUser0).unregisterReceiver(broadcastReceiver)
+        verify(mockUBRUser1, never()).unregisterReceiver(broadcastReceiver)
+    }
+
+    private class TestBroadcastDispatcher(
+        context: Context,
+        mainHandler: Handler,
+        bgLooper: Looper,
+        var mockUBRMap: Map<Int, UserBroadcastDispatcher>
+    ) : BroadcastDispatcher(context, mainHandler, bgLooper) {
+        override fun createUBRForUser(userId: Int): UserBroadcastDispatcher {
+            return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java))
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
new file mode 100644
index 0000000..011c2cd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2019 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.broadcast
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.Handler
+import android.os.UserHandle
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import com.android.systemui.SysuiTestCase
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+@SmallTest
+class UserBroadcastDispatcherTest : SysuiTestCase() {
+
+    companion object {
+        private const val ACTION_1 = "com.android.systemui.tests.ACTION_1"
+        private const val ACTION_2 = "com.android.systemui.tests.ACTION_2"
+        private const val CATEGORY_1 = "com.android.systemui.tests.CATEGORY_1"
+        private const val CATEGORY_2 = "com.android.systemui.tests.CATEGORY_2"
+        private const val USER_ID = 0
+        private val USER_HANDLE = UserHandle.of(USER_ID)
+
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+    }
+
+    @Mock
+    private lateinit var broadcastReceiver: BroadcastReceiver
+    @Mock
+    private lateinit var broadcastReceiverOther: BroadcastReceiver
+    @Mock
+    private lateinit var mockContext: Context
+    @Mock
+    private lateinit var mockHandler: Handler
+
+    @Captor
+    private lateinit var argumentCaptor: ArgumentCaptor<IntentFilter>
+
+    private lateinit var testableLooper: TestableLooper
+    private lateinit var universalBroadcastReceiver: UserBroadcastDispatcher
+    private lateinit var intentFilter: IntentFilter
+    private lateinit var intentFilterOther: IntentFilter
+    private lateinit var handler: Handler
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        testableLooper = TestableLooper.get(this)
+        handler = Handler(testableLooper.looper)
+
+        universalBroadcastReceiver = UserBroadcastDispatcher(
+                mockContext, USER_ID, handler, testableLooper.looper)
+    }
+
+    @Test
+    fun testNotRegisteredOnStart() {
+        testableLooper.processAllMessages()
+        verify(mockContext, never()).registerReceiver(any(), any())
+        verify(mockContext, never()).registerReceiver(any(), any(), anyInt())
+        verify(mockContext, never()).registerReceiver(any(), any(), anyString(), any())
+        verify(mockContext, never()).registerReceiver(any(), any(), anyString(), any(), anyInt())
+        verify(mockContext, never()).registerReceiverAsUser(any(), any(), any(), anyString(), any())
+    }
+
+    @Test
+    fun testSingleReceiverRegistered() {
+        intentFilter = IntentFilter(ACTION_1)
+
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+        testableLooper.processAllMessages()
+
+        assertTrue(universalBroadcastReceiver.isRegistered())
+        verify(mockContext).registerReceiverAsUser(
+                any(),
+                eq(USER_HANDLE),
+                capture(argumentCaptor),
+                any(),
+                any())
+        assertEquals(1, argumentCaptor.value.countActions())
+        assertTrue(argumentCaptor.value.hasAction(ACTION_1))
+        assertEquals(0, argumentCaptor.value.countCategories())
+    }
+
+    @Test
+    fun testSingleReceiverUnregistered() {
+        intentFilter = IntentFilter(ACTION_1)
+
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+        testableLooper.processAllMessages()
+        reset(mockContext)
+
+        assertTrue(universalBroadcastReceiver.isRegistered())
+
+        universalBroadcastReceiver.unregisterReceiver(broadcastReceiver)
+        testableLooper.processAllMessages()
+
+        verify(mockContext, atLeastOnce()).unregisterReceiver(any())
+        verify(mockContext, never()).registerReceiverAsUser(any(), any(), any(), any(), any())
+        assertFalse(universalBroadcastReceiver.isRegistered())
+    }
+
+    @Test
+    fun testFilterHasAllActionsAndCategories_twoReceivers() {
+        intentFilter = IntentFilter(ACTION_1)
+        intentFilterOther = IntentFilter(ACTION_2).apply {
+            addCategory(CATEGORY_1)
+            addCategory(CATEGORY_2)
+        }
+
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilter, mockHandler, USER_HANDLE))
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiverOther, intentFilterOther, mockHandler, USER_HANDLE))
+
+        testableLooper.processAllMessages()
+        assertTrue(universalBroadcastReceiver.isRegistered())
+
+        verify(mockContext, times(2)).registerReceiverAsUser(
+                any(),
+                eq(USER_HANDLE),
+                capture(argumentCaptor),
+                any(),
+                any())
+
+        val lastFilter = argumentCaptor.value
+
+        assertTrue(lastFilter.hasAction(ACTION_1))
+        assertTrue(lastFilter.hasAction(ACTION_2))
+        assertTrue(lastFilter.hasCategory(CATEGORY_1))
+        assertTrue(lastFilter.hasCategory(CATEGORY_1))
+    }
+
+    @Test
+    fun testDispatchToCorrectReceiver() {
+        intentFilter = IntentFilter(ACTION_1)
+        intentFilterOther = IntentFilter(ACTION_2)
+
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+
+        val intent = Intent(ACTION_2)
+
+        universalBroadcastReceiver.onReceive(mockContext, intent)
+        testableLooper.processAllMessages()
+
+        verify(broadcastReceiver, never()).onReceive(any(), any())
+        verify(broadcastReceiverOther).onReceive(mockContext, intent)
+    }
+
+    @Test
+    fun testDispatchToCorrectReceiver_differentFiltersSameReceiver() {
+        intentFilter = IntentFilter(ACTION_1)
+        intentFilterOther = IntentFilter(ACTION_2)
+
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilterOther, handler, USER_HANDLE))
+
+        val intent = Intent(ACTION_2)
+
+        universalBroadcastReceiver.onReceive(mockContext, intent)
+        testableLooper.processAllMessages()
+
+        verify(broadcastReceiver).onReceive(mockContext, intent)
+    }
+
+    @Test
+    fun testDispatchIntentWithoutCategories() {
+        intentFilter = IntentFilter(ACTION_1)
+        intentFilter.addCategory(CATEGORY_1)
+        intentFilterOther = IntentFilter(ACTION_1)
+        intentFilterOther.addCategory(CATEGORY_2)
+
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiver, intentFilter, handler, USER_HANDLE))
+        universalBroadcastReceiver.registerReceiver(
+                ReceiverData(broadcastReceiverOther, intentFilterOther, handler, USER_HANDLE))
+
+        val intent = Intent(ACTION_1)
+
+        universalBroadcastReceiver.onReceive(mockContext, intent)
+        testableLooper.processAllMessages()
+
+        verify(broadcastReceiver).onReceive(mockContext, intent)
+        verify(broadcastReceiverOther).onReceive(mockContext, intent)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
index 25a1a75..fb4c1ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
@@ -133,8 +133,8 @@
         // This test looks just like testPass_horizontalZigZagVerticalStraight but with
         // a shorter y range, making it look more crooked.
         appendMoveEvent(0, 0);
-        appendMoveEvent(5, 10);
-        appendMoveEvent(-5, 20);
+        appendMoveEvent(6, 10);
+        appendMoveEvent(-6, 20);
         assertThat(mClassifier.isFalseTouch(), is(true));
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 6dfb19e..1e18e51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -45,11 +45,14 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -58,6 +61,8 @@
 
     DozeMachine mMachine;
 
+    @Mock
+    private WakefulnessLifecycle mWakefulnessLifecycle;
     private DozeServiceFake mServiceFake;
     private WakeLockFake mWakeLockFake;
     private AmbientDisplayConfiguration mConfigMock;
@@ -65,12 +70,13 @@
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mServiceFake = new DozeServiceFake();
         mWakeLockFake = new WakeLockFake();
         mConfigMock = mock(AmbientDisplayConfiguration.class);
         mPartMock = mock(DozeMachine.Part.class);
 
-        mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake);
+        mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, mWakefulnessLifecycle);
 
         mMachine.setParts(new DozeMachine.Part[]{mPartMock});
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 1e1f2156..b252a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -365,4 +365,45 @@
         waitForIdleSync();
         verify(mCallbacks).onRecentsAnimationStateChanged(eq(true));
     }
+
+    @Test
+    public void testShowBiometricDialog() {
+        Bundle bundle = new Bundle();
+        String packageName = "test";
+        mCommandQueue.showBiometricDialog(bundle, null /* receiver */, 1, true, 3, packageName);
+        waitForIdleSync();
+        verify(mCallbacks).showBiometricDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
+                eq(packageName));
+    }
+
+    @Test
+    public void testOnBiometricAuthenticated() {
+        String failureReason = "test_failure_reason";
+        mCommandQueue.onBiometricAuthenticated(true /* authenticated */, failureReason);
+        waitForIdleSync();
+        verify(mCallbacks).onBiometricAuthenticated(eq(true), eq(failureReason));
+    }
+
+    @Test
+    public void testOnBiometricHelp() {
+        String helpMessage = "test_help_message";
+        mCommandQueue.onBiometricHelp(helpMessage);
+        waitForIdleSync();
+        verify(mCallbacks).onBiometricHelp(eq(helpMessage));
+    }
+
+    @Test
+    public void testOnBiometricError() {
+        String errorMessage = "test_error_message";
+        mCommandQueue.onBiometricError(errorMessage);
+        waitForIdleSync();
+        verify(mCallbacks).onBiometricError(eq(errorMessage));
+    }
+
+    @Test
+    public void testHideBiometricDialog() {
+        mCommandQueue.hideBiometricDialog();
+        waitForIdleSync();
+        verify(mCallbacks).hideBiometricDialog();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 0dbf308..97ad47e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -47,6 +47,7 @@
 import com.android.internal.util.function.TriConsumer;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.utils.os.FakeHandler;
 
@@ -96,7 +97,8 @@
                     mScrimBehindAlpha = scrimBehindAlpha;
                     mScrimInFrontColor = scrimInFrontColor;
                 },
-                visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager);
+                visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager,
+                mock(KeyguardMonitor.class));
         mScrimController.setHasBackdrop(false);
         mScrimController.setWallpaperSupportsAmbientMode(false);
         mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -681,9 +683,9 @@
         SynchronousScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
                 TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
                 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
-                AlarmManager alarmManager) {
+                AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
             super(scrimBehind, scrimInFront, scrimStateListener, scrimVisibleListener,
-                    dozeParameters, alarmManager);
+                    dozeParameters, alarmManager, keyguardMonitor);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
index 3e4c4d6..556ed5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HotspotControllerImplTest.java
@@ -73,6 +73,7 @@
                 any(Handler.class));
 
         mController = new HotspotControllerImpl(mContext, new Handler(mLooper.getLooper()));
+        mController.handleSetListening(true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
index 016160a..c9681ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -26,6 +26,10 @@
     }
 
     @Override
+    public void handleSetListening(boolean listening) {
+    }
+
+    @Override
     public boolean isHotspotEnabled() {
         return false;
     }
diff --git a/services/Android.bp b/services/Android.bp
index b08d1a8..54157d0 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -35,6 +35,7 @@
         "services.usage",
         "services.usb",
         "services.voiceinteraction",
+        "services.wifi",
         "android.hidl.base-V1.0-java",
     ],
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 6b88f5a..1356157 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1186,8 +1186,8 @@
         mInvocationHandler.notifySoftKeyboardShowModeChangedLocked(showState);
     }
 
-    public void notifyAccessibilityButtonClickedLocked() {
-        mInvocationHandler.notifyAccessibilityButtonClickedLocked();
+    public void notifyAccessibilityButtonClickedLocked(int displayId) {
+        mInvocationHandler.notifyAccessibilityButtonClickedLocked(displayId);
     }
 
     public void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) {
@@ -1226,11 +1226,11 @@
         }
     }
 
-    private void notifyAccessibilityButtonClickedInternal() {
+    private void notifyAccessibilityButtonClickedInternal(int displayId) {
         final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
         if (listener != null) {
             try {
-                listener.onAccessibilityButtonClicked();
+                listener.onAccessibilityButtonClicked(displayId);
             } catch (RemoteException re) {
                 Slog.e(LOG_TAG, "Error sending accessibility button click to " + mService, re);
             }
@@ -1484,7 +1484,8 @@
                 } break;
 
                 case MSG_ON_ACCESSIBILITY_BUTTON_CLICKED: {
-                    notifyAccessibilityButtonClickedInternal();
+                    final int displayId = (int) message.arg1;
+                    notifyAccessibilityButtonClickedInternal(displayId);
                 } break;
 
                 case MSG_ON_ACCESSIBILITY_BUTTON_AVAILABILITY_CHANGED: {
@@ -1546,8 +1547,8 @@
             mIsSoftKeyboardCallbackEnabled = enabled;
         }
 
-        public void notifyAccessibilityButtonClickedLocked() {
-            final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_CLICKED);
+        public void notifyAccessibilityButtonClickedLocked(int displayId) {
+            final Message msg = obtainMessage(MSG_ON_ACCESSIBILITY_BUTTON_CLICKED, displayId, 0);
             msg.sendToTarget();
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 18b6f90..c733d3b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -29,8 +29,8 @@
 import android.view.MotionEvent;
 import android.view.accessibility.AccessibilityEvent;
 
-import com.android.server.accessibility.gestures.TouchExplorer;
 import com.android.server.LocalServices;
+import com.android.server.accessibility.gestures.TouchExplorer;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.util.ArrayList;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index b5b3cd2..893e4e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1085,9 +1085,7 @@
                 for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
                     final AccessibilityServiceConnection service = state.mBoundServices.get(i);
                     if (service.mRequestAccessibilityButton) {
-                        // TODO(b/120762691): Need to notify each accessibility service if
-                        // accessibility button is clicked per display.
-                        service.notifyAccessibilityButtonClickedLocked();
+                        service.notifyAccessibilityButtonClickedLocked(displayId);
                         return;
                     }
                 }
@@ -1109,9 +1107,7 @@
                     final AccessibilityServiceConnection service = state.mBoundServices.get(i);
                     if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
                             state.mServiceAssignedToAccessibilityButton))) {
-                        // TODO(b/120762691): Need to notify each accessibility service if
-                        // accessibility button is clicked per display.
-                        service.notifyAccessibilityButtonClickedLocked();
+                        service.notifyAccessibilityButtonClickedLocked(displayId);
                         return;
                     }
                 }
@@ -2535,10 +2531,9 @@
                     // to create event handler per display. The events should be handled by the
                     // display which is overlaid by it.
                     final Display display = displays[i];
-                    if (display.getType() == Display.TYPE_OVERLAY) {
-                        continue;
+                    if (isValidDisplay(display)) {
+                        mDisplaysList.add(display);
                     }
-                    mDisplaysList.add(display);
                 }
             }
         }
@@ -2546,7 +2541,7 @@
         @Override
         public void onDisplayAdded(int displayId) {
             final Display display = mDisplayManager.getDisplay(displayId);
-            if (display == null || display.getType() == Display.TYPE_OVERLAY) {
+            if (!isValidDisplay(display)) {
                 return;
             }
 
@@ -2597,6 +2592,19 @@
         public void onDisplayChanged(int displayId) {
             /* do nothing */
         }
+
+        private boolean isValidDisplay(@Nullable Display display) {
+            if (display == null || display.getType() == Display.TYPE_OVERLAY) {
+                return false;
+            }
+            // Private virtual displays are created by the ap and is not allowed to access by other
+            // aps. We assume we could ignore them.
+            if ((display.getType() == Display.TYPE_VIRTUAL
+                    && (display.getFlags() & Display.FLAG_PRIVATE) != 0)) {
+                return false;
+            }
+            return true;
+        }
     }
 
     /** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 9687098..c9efe36 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -872,8 +872,8 @@
                 RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
                         windowId, connection, packageName, resolvedUid, resolvedUserId);
                 wrapper.linkToDeath();
-                getInteractionConnectionsForUserLocked(userId).put(windowId, wrapper);
-                getWindowTokensForUserLocked(userId).put(windowId, windowToken.asBinder());
+                getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper);
+                getWindowTokensForUserLocked(resolvedUserId).put(windowId, windowToken.asBinder());
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid()
                             + " with windowId: " + windowId
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 10c32ee..d8b7e3a 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -27,8 +27,6 @@
 import android.util.Slog;
 import android.view.InputDevice;
 import android.view.MotionEvent;
-import android.view.MotionEvent.PointerCoords;
-import android.view.MotionEvent.PointerProperties;
 import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -129,15 +127,6 @@
     // Context in which this explorer operates.
     private final Context mContext;
 
-    // The long pressing pointer id if coordinate remapping is needed.
-    private int mLongPressingPointerId = -1;
-
-    // The long pressing pointer X if coordinate remapping is needed.
-    private int mLongPressingPointerDeltaX;
-
-    // The long pressing pointer Y if coordinate remapping is needed.
-    private int mLongPressingPointerDeltaY;
-
 
 /**
      * Creates a new instance.
@@ -231,10 +220,6 @@
         mGestureDetector.clear();
         // Go to initial state.
         mState.clear();
-        // Clear the long pressing pointer remap data.
-        mLongPressingPointerId = -1;
-        mLongPressingPointerDeltaX = 0;
-        mLongPressingPointerDeltaY = 0;
         mAms.onTouchInteractionEnd();
     }
 
@@ -705,17 +690,6 @@
                 return;
             }
             case MotionEvent.ACTION_UP: {
-                // Offset the event if we are doing a long press as the
-                // target is not necessarily under the user's finger.
-                if (mLongPressingPointerId >= 0) {
-                    event = offsetEvent(event, - mLongPressingPointerDeltaX,
-                            - mLongPressingPointerDeltaY);
-                    // Clear the long press state.
-                    mLongPressingPointerId = -1;
-                    mLongPressingPointerDeltaX = 0;
-                    mLongPressingPointerDeltaY = 0;
-                }
-
                 // Deliver the event.
                 sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
 
@@ -865,18 +839,6 @@
         } else {
             event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
         }
-
-        // If the user is long pressing but the long pressing pointer
-        // was not exactly over the accessibility focused item we need
-        // to remap the location of that pointer so the user does not
-        // have to explicitly touch explore something to be able to
-        // long press it, or even worse to avoid the user long pressing
-        // on the wrong item since click and long press behave differently.
-        if (mLongPressingPointerId >= 0) {
-            event = offsetEvent(event, - mLongPressingPointerDeltaX,
-                    - mLongPressingPointerDeltaY);
-        }
-
         if (DEBUG) {
             Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
                     + Integer.toHexString(policyFlags));
@@ -897,39 +859,6 @@
     }
 
     /**
-     * Offsets all pointers in the given event by adding the specified X and Y
-     * offsets.
-     *
-     * @param event The event to offset.
-     * @param offsetX The X offset.
-     * @param offsetY The Y offset.
-     * @return An event with the offset pointers or the original event if both
-     *         offsets are zero.
-     */
-    private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
-        if (offsetX == 0 && offsetY == 0) {
-            return event;
-        }
-        final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
-        final int pointerCount = event.getPointerCount();
-        PointerProperties[] props = PointerProperties.createArray(pointerCount);
-        PointerCoords[] coords = PointerCoords.createArray(pointerCount);
-        for (int i = 0; i < pointerCount; i++) {
-            event.getPointerProperties(i, props[i]);
-            event.getPointerCoords(i, coords[i]);
-            if (i == remappedIndex) {
-                coords[i].x += offsetX;
-                coords[i].y += offsetY;
-            }
-        }
-        return MotionEvent.obtain(event.getDownTime(),
-                event.getEventTime(), event.getAction(), event.getPointerCount(),
-                props, coords, event.getMetaState(), event.getButtonState(),
-                1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
-                event.getSource(), event.getDisplayId(), event.getFlags());
-    }
-
-    /**
      * Computes the action for an injected event based on a masked action
      * and a pointer index.
      *
@@ -1189,9 +1118,6 @@
                 + ", mDetermineUserIntentTimeout: " + mDetermineUserIntentTimeout
                 + ", mDoubleTapSlop: " + mDoubleTapSlop
                 + ", mDraggingPointerId: " + mDraggingPointerId
-                + ", mLongPressingPointerId: " + mLongPressingPointerId
-                + ", mLongPressingPointerDeltaX: " + mLongPressingPointerDeltaX
-                + ", mLongPressingPointerDeltaY: " + mLongPressingPointerDeltaY
                 + ", mTempPoint: " + mTempPoint
                 + " }";
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8032e1b..4579a3d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -301,6 +301,11 @@
                         + "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
                 return;
             }
+            if (mCurrentViewId == null) {
+                Slog.w(TAG, "No current view id - session might have finished");
+                return;
+            }
+
             final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE);
             if (structure == null) {
                 Slog.e(TAG, "No assist structure - app might have crashed providing it");
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index dc0bdb3..222a6f2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -18,11 +18,15 @@
 
 import static com.android.internal.util.Preconditions.checkNotNull;
 
+import static java.util.Collections.emptySet;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
 import android.app.backup.BackupManager;
+import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
 import android.app.backup.IBackupObserver;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -31,24 +35,37 @@
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.server.SystemConfig;
 import com.android.server.SystemService;
+import com.android.server.backup.utils.RandomAccessFileUtils;
 
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.Set;
 
@@ -58,8 +75,20 @@
  * <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
  * incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
  * corresponding backup/restore operation.
+ *
+ * <p>It also determines whether the backup service is available. It can be disabled in the
+ * following two ways:
+ *
+ * <ul>
+ *  <li>Temporary - call {@link #setBackupServiceActive(int, boolean)}, or
+ *  <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
+ * </ul>
+ *
+ * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
+ * privileged callers (currently {@link DevicePolicyManager}). If called on {@link
+ * UserHandle#USER_SYSTEM}, backup is disabled for all users.
  */
-public class BackupManagerService {
+public class BackupManagerService extends IBackupManager.Stub {
     public static final String TAG = "BackupManagerService";
     public static final boolean DEBUG = true;
     public static final boolean MORE_DEBUG = false;
@@ -68,50 +97,249 @@
     @VisibleForTesting
     static final String DUMP_RUNNING_USERS_MESSAGE = "Backup Manager is running for users:";
 
+    /**
+     * Name of file that disables the backup service. If this file exists, then backup is disabled
+     * for all users.
+     */
+    private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
+
+    /**
+     * Name of file for non-system users that enables the backup service for the user. Backup is
+     * disabled by default in non-system users.
+     */
+    private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+
+    /**
+     * Name of file for non-system users that remembers whether backup was explicitly activated or
+     * deactivated with a call to setBackupServiceActive.
+     */
+    private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
+
+    // Product-level suppression of backup/restore.
+    private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
+
+    private static final String BACKUP_THREAD = "backup";
+
+    static BackupManagerService sInstance;
+
+    static BackupManagerService getInstance() {
+        return checkNotNull(sInstance);
+    }
+
     private final Context mContext;
-    private final Trampoline mTrampoline;
+    private final UserManager mUserManager;
 
-    // Keeps track of all unlocked users registered with this service. Indexed by user id.
-    private final SparseArray<UserBackupManagerService> mServiceUsers = new SparseArray<>();
+    private final boolean mGlobalDisable;
+    // Lock to write backup suppress files.
+    // TODD(b/121198006): remove this object and synchronized all methods on "this".
+    private final Object mStateLock = new Object();
 
-    /** Instantiate a new instance of {@link BackupManagerService}. */
-    public BackupManagerService(Context context, Trampoline trampoline) {
-        mContext = checkNotNull(context);
-        mTrampoline = checkNotNull(trampoline);
+    private final Handler mHandler;
+    private final Set<ComponentName> mTransportWhitelist;
+
+    /** Keeps track of all unlocked users registered with this service. Indexed by user id. */
+    private final SparseArray<UserBackupManagerService> mUserServices;
+
+    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                if (userId > 0) { // for only non system users
+                    mHandler.post(() -> onRemovedNonSystemUser(userId));
+                }
+            }
+        }
+    };
+
+    public BackupManagerService(Context context) {
+        this(context, new SparseArray<>());
+    }
+
+    @VisibleForTesting
+    BackupManagerService(Context context, SparseArray<UserBackupManagerService> userServices) {
+        mContext = context;
+        mGlobalDisable = isBackupDisabled();
+        HandlerThread handlerThread =
+                new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
+        handlerThread.start();
+        mHandler = new Handler(handlerThread.getLooper());
+        mUserManager = UserManager.get(context);
+        mUserServices = userServices;
+        Set<ComponentName> transportWhitelist =
+                SystemConfig.getInstance().getBackupTransportWhitelist();
+        mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
+        mContext.registerReceiver(
+                mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
+    }
+
+    // TODO: Remove this when we implement DI by injecting in the construtor.
+    @VisibleForTesting
+    Handler getBackupHandler() {
+        return mHandler;
+    }
+
+    protected boolean isBackupDisabled() {
+        return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
+    }
+
+    protected int binderGetCallingUserId() {
+        return Binder.getCallingUserHandle().getIdentifier();
+    }
+
+    protected int binderGetCallingUid() {
+        return Binder.getCallingUid();
+    }
+
+    /** Stored in the system user's directory. */
+    protected File getSuppressFileForSystemUser() {
+        return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
+                BACKUP_SUPPRESS_FILENAME);
+    }
+
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    protected File getRememberActivatedFileForNonSystemUser(int userId) {
+        return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
+    }
+
+    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+    protected File getActivatedFileForNonSystemUser(int userId) {
+        return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
     }
 
     /**
-     * If {@code userId} is different from the calling user id, then the caller must hold the
-     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
-     *
-     * @param userId User id on which the backup operation is being requested.
-     * @param message A message to include in the exception if it is thrown.
+     * Remove backup state for non system {@code userId} when the user is removed from the device.
+     * For non system users, backup state is stored in both the user's own dir and the system dir.
+     * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
+     * the part of the user backup state which is in the system dir also gets removed.
      */
-    private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
-        if (Binder.getCallingUserHandle().getIdentifier() != userId) {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+    private void onRemovedNonSystemUser(int userId) {
+        Slog.i(TAG, "Removing state for non system user " + userId);
+        File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
+        if (!FileUtils.deleteContentsAndDir(dir)) {
+            Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
         }
     }
 
-    // ---------------------------------------------
-    // USER LIFECYCLE CALLBACKS
-    // ---------------------------------------------
+    // TODO (b/124359804) move to util method in FileUtils
+    private void createFile(File file) throws IOException {
+        if (file.exists()) {
+            return;
+        }
+
+        file.getParentFile().mkdirs();
+        if (!file.createNewFile()) {
+            Slog.w(TAG, "Failed to create file " + file.getPath());
+        }
+    }
+
+    // TODO (b/124359804) move to util method in FileUtils
+    private void deleteFile(File file) {
+        if (!file.exists()) {
+            return;
+        }
+
+        if (!file.delete()) {
+            Slog.w(TAG, "Failed to delete file " + file.getPath());
+        }
+    }
+
+    /**
+     * Deactivates the backup service for user {@code userId}. If this is the system user, it
+     * creates a suppress file which disables backup for all users. If this is a non-system user, it
+     * only deactivates backup for that user by deleting its activate file.
+     */
+    @GuardedBy("mStateLock")
+    private void deactivateBackupForUserLocked(int userId) throws IOException {
+        if (userId == UserHandle.USER_SYSTEM) {
+            createFile(getSuppressFileForSystemUser());
+        } else {
+            deleteFile(getActivatedFileForNonSystemUser(userId));
+        }
+    }
+
+    /**
+     * Enables the backup service for user {@code userId}. If this is the system user, it deletes
+     * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
+     * deleting the suppress file does not automatically enable backup for non-system users, they
+     * need their own activate file in order to participate in the service.
+     */
+    @GuardedBy("mStateLock")
+    private void activateBackupForUserLocked(int userId) throws IOException {
+        if (userId == UserHandle.USER_SYSTEM) {
+            deleteFile(getSuppressFileForSystemUser());
+        } else {
+            createFile(getActivatedFileForNonSystemUser(userId));
+        }
+    }
+
+    // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
+    // it's used in multiple places where I/O waits would cause system lock-ups.
+    private boolean isUserReadyForBackup(int userId) {
+        return mUserServices.get(UserHandle.USER_SYSTEM) != null
+                && mUserServices.get(userId) != null;
+    }
+
+    /**
+     * Backup is activated for the system user if the suppress file does not exist. Backup is
+     * activated for non-system users if the suppress file does not exist AND the user's activated
+     * file exists.
+     */
+    private boolean isBackupActivatedForUser(int userId) {
+        if (getSuppressFileForSystemUser().exists()) {
+            return false;
+        }
+
+        return userId == UserHandle.USER_SYSTEM
+                || getActivatedFileForNonSystemUser(userId).exists();
+    }
+
+    protected Context getContext() {
+        return mContext;
+    }
+
+    protected UserManager getUserManager() {
+        return mUserManager;
+    }
+
+    protected void postToHandler(Runnable runnable) {
+        mHandler.post(runnable);
+    }
+
+    /**
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
+     * Starts the backup service for this user if backup is active for this user. Offloads work onto
+     * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
+     * essential for device functioning.
+     */
+    void onUnlockUser(int userId) {
+        postToHandler(() -> startServiceForUser(userId));
+    }
 
     /**
      * Starts the backup service for user {@code userId} by creating a new instance of {@link
      * UserBackupManagerService} and registering it with this service.
      */
     @VisibleForTesting
-    protected void startServiceForUser(int userId, Set<ComponentName> transportWhitelist) {
-        if (mServiceUsers.get(userId) != null) {
+    void startServiceForUser(int userId) {
+        // We know that the user is unlocked here because it is called from setBackupServiceActive
+        // and unlockUser which have these guarantees. So we can check if the file exists.
+        if (mGlobalDisable) {
+            Slog.i(TAG, "Backup service not supported");
+            return;
+        }
+        if (!isBackupActivatedForUser(userId)) {
+            Slog.i(TAG, "Backup not activated for user " + userId);
+            return;
+        }
+        if (mUserServices.get(userId) != null) {
             Slog.i(TAG, "userId " + userId + " already started, so not starting again");
             return;
         }
-
+        Slog.i(TAG, "Starting service for user: " + userId);
         UserBackupManagerService userBackupManagerService =
                 UserBackupManagerService.createAndInitializeService(
-                        userId, mContext, mTrampoline, transportWhitelist);
+                        userId, mContext, this, mTransportWhitelist);
         startServiceForUser(userId, userBackupManagerService);
     }
 
@@ -120,7 +348,7 @@
      * UserBackupManagerService} with this service and setting enabled state.
      */
     void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
-        mServiceUsers.put(userId, userBackupManagerService);
+        mUserServices.put(userId, userBackupManagerService);
 
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
         userBackupManagerService.initializeBackupEnableState();
@@ -130,7 +358,7 @@
     /** Stops the backup service for user {@code userId} when the user is stopped. */
     @VisibleForTesting
     protected void stopServiceForUser(int userId) {
-        UserBackupManagerService userBackupManagerService = mServiceUsers.removeReturnOld(userId);
+        UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);
 
         if (userBackupManagerService != null) {
             userBackupManagerService.tearDownService();
@@ -140,11 +368,6 @@
         }
     }
 
-    boolean isAbleToServeUser(int userId) {
-        return getUserServices().get(UserHandle.USER_SYSTEM) != null
-                && getUserServices().get(userId) != null;
-    }
-
     /**
      *  Returns a list of users currently unlocked that have a {@link UserBackupManagerService}
      *  registered.
@@ -154,43 +377,146 @@
      *  TODO: Return a copy or only expose read-only information through other means.
      */
     @VisibleForTesting
-    public SparseArray<UserBackupManagerService> getUserServices() {
-        return mServiceUsers;
+    SparseArray<UserBackupManagerService> getUserServices() {
+        return mUserServices;
     }
 
     /**
-     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
-     * If the user is not registered with the service (either the user is locked or not eligible for
-     * the backup service) then return {@code null}.
-     *
-     * @param userId The id of the user to retrieve its instance of {@link
-     *     UserBackupManagerService}.
-     * @param caller A {@link String} identifying the caller for logging purposes.
-     * @throws SecurityException if {@code userId} is different from the calling user id and the
-     *     caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
+     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
      */
-    @Nullable
-    @VisibleForTesting
-    UserBackupManagerService getServiceForUserIfCallerHasPermission(
-            @UserIdInt int userId, String caller) {
-        enforceCallingPermissionOnUserId(userId, caller);
-        UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
-        if (userBackupManagerService == null) {
-            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
-        }
-        return userBackupManagerService;
+    void onStopUser(int userId) {
+        postToHandler(
+                () -> {
+                    if (!mGlobalDisable) {
+                        Slog.i(TAG, "Stopping service for user: " + userId);
+                        stopServiceForUser(userId);
+                    }
+                });
     }
 
-    /*
-     * The following methods are implementations of IBackupManager methods called from Trampoline.
-     * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
-     * action on the passed in user. Currently this is a straight redirection (see TODO).
-     */
-    // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
+    /** Returns {@link UserBackupManagerService} for user {@code userId}. */
+    @Nullable
+    public UserBackupManagerService getUserService(int userId) {
+        return mUserServices.get(userId);
+    }
 
-    // ---------------------------------------------
-    // BACKUP AGENT OPERATIONS
-    // ---------------------------------------------
+    /**
+     * The system user and managed profiles can only be acted on by callers in the system or root
+     * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
+     * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
+     */
+    private void enforcePermissionsOnUser(int userId) throws SecurityException {
+        boolean isRestrictedUser =
+                userId == UserHandle.USER_SYSTEM
+                        || getUserManager().getUserInfo(userId).isManagedProfile();
+
+        if (isRestrictedUser) {
+            int caller = binderGetCallingUid();
+            if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
+                throw new SecurityException("No permission to configure backup activity");
+            }
+        } else {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.BACKUP, "No permission to configure backup activity");
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                    "No permission to configure backup activity");
+        }
+    }
+
+    /**
+     * Only privileged callers should be changing the backup state. Deactivating backup in the
+     * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
+     * is unlocked at this point yet, so handle both cases.
+     */
+    public void setBackupServiceActive(int userId, boolean makeActive) {
+        enforcePermissionsOnUser(userId);
+
+        // In Q, backup is OFF by default for non-system users. In the future, we will change that
+        // to ON unless backup was explicitly deactivated with a (permissioned) call to
+        // setBackupServiceActive.
+        // Therefore, remember this for use in the future. Basically the default in the future will
+        // be: rememberFile.exists() ? rememberFile.value() : ON
+        // Note that this has to be done right after the permission checks and before any other
+        // action since we need to remember that a permissioned call was made irrespective of
+        // whether the call changes the state or not.
+        if (userId != UserHandle.USER_SYSTEM) {
+            try {
+                File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
+                createFile(rememberFile);
+                RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
+            } catch (IOException e) {
+                Slog.e(TAG, "Unable to persist backup service activity", e);
+            }
+        }
+
+        if (mGlobalDisable) {
+            Slog.i(TAG, "Backup service not supported");
+            return;
+        }
+
+        synchronized (mStateLock) {
+            Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
+            if (makeActive) {
+                try {
+                    activateBackupForUserLocked(userId);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to persist backup service activity");
+                }
+
+                // If the user is unlocked, we can start the backup service for it. Otherwise we
+                // will start the service when the user is unlocked as part of its unlock callback.
+                if (getUserManager().isUserUnlocked(userId)) {
+                    // Clear calling identity as initialization enforces the system identity but we
+                    // can be coming from shell.
+                    long oldId = Binder.clearCallingIdentity();
+                    try {
+                        startServiceForUser(userId);
+                    } finally {
+                        Binder.restoreCallingIdentity(oldId);
+                    }
+                }
+            } else {
+                try {
+                    //TODO(b/121198006): what if this throws an exception?
+                    deactivateBackupForUserLocked(userId);
+                } catch (IOException e) {
+                    Slog.e(TAG, "Unable to persist backup service inactivity");
+                }
+                //TODO(b/121198006): loop through active users that have work profile and
+                // stop them as well.
+                onStopUser(userId);
+            }
+        }
+    }
+
+    // IBackupManager binder API
+
+    /**
+     * Querying activity state of backup service.
+     *
+     * @param userId The user in which the activity state of backup service is queried.
+     * @return true if the service is active.
+     */
+    @Override
+    public boolean isBackupServiceActive(int userId) {
+        synchronized (mStateLock) {
+            return !mGlobalDisable && isBackupActivatedForUser(userId);
+        }
+    }
+
+    @Override
+    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            dataChanged(userId, packageName);
+        }
+    }
+
+    @Override
+    public void dataChanged(String packageName) throws RemoteException {
+        dataChangedForUser(binderGetCallingUserId(), packageName);
+    }
 
     /**
      * An app's backup agent calls this method to let the service know that there's new data to
@@ -206,49 +532,18 @@
         }
     }
 
-    /**
-     * Callback: a requested backup agent has been instantiated. This should only be called from the
-     * {@link ActivityManager}.
-     */
-    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.agentConnected(packageName, agentBinder);
-        }
-    }
-
-    /**
-     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
-     * called from the {@link ActivityManager}.
-     */
-    public void agentDisconnected(@UserIdInt int userId, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.agentDisconnected(packageName);
-        }
-    }
-
-    /**
-     * Used by a currently-active backup agent to notify the service that it has completed its given
-     * outstanding asynchronous backup/restore operation.
-     */
-    public void opComplete(@UserIdInt int userId, int token, long result) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "opComplete()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.opComplete(token, result);
-        }
-    }
-
     // ---------------------------------------------
     // TRANSPORT OPERATIONS
     // ---------------------------------------------
 
+    @Override
+    public void initializeTransportsForUser(
+            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            initializeTransports(userId, transportNames, observer);
+        }
+    }
+
     /** Run an initialize operation for the given transports {@code transportNames}. */
     public void initializeTransports(
             @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
@@ -260,6 +555,14 @@
         }
     }
 
+    @Override
+    public void clearBackupDataForUser(int userId, String transportName, String packageName)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            clearBackupData(userId, transportName, packageName);
+        }
+    }
+
     /**
      * Clear the given package {@code packageName}'s backup data from the transport {@code
      * transportName}.
@@ -273,6 +576,340 @@
         }
     }
 
+    @Override
+    public void clearBackupData(String transportName, String packageName)
+            throws RemoteException {
+        clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+    }
+
+    @Override
+    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            agentConnected(userId, packageName, agent);
+        }
+    }
+
+    @Override
+    public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+    }
+
+    /**
+     * Callback: a requested backup agent has been instantiated. This should only be called from the
+     * {@link ActivityManager}.
+     */
+    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentConnected(packageName, agentBinder);
+        }
+    }
+
+    @Override
+    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            agentDisconnected(userId, packageName);
+        }
+    }
+
+    @Override
+    public void agentDisconnected(String packageName) throws RemoteException {
+        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+    }
+
+    /**
+     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+     * called from the {@link ActivityManager}.
+     */
+    public void agentDisconnected(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.agentDisconnected(packageName);
+        }
+    }
+
+    @Override
+    public void restoreAtInstallForUser(int userId, String packageName, int token)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            restoreAtInstall(userId, packageName, token);
+        }
+    }
+
+    @Override
+    public void restoreAtInstall(String packageName, int token) throws RemoteException {
+        restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
+    }
+
+    /**
+     * Used to run a restore pass for an application that is being installed. This should only be
+     * called from the {@link PackageManager}.
+     */
+    public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.restoreAtInstall(packageName, token);
+        }
+    }
+
+    @Override
+    public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            setBackupEnabled(userId, isEnabled);
+        }
+    }
+
+    @Override
+    public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+        setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
+    }
+
+    /** Enable/disable the backup service. This is user-configurable via backup settings. */
+    public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setBackupEnabled(enable);
+        }
+    }
+
+    @Override
+    public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            setAutoRestore(userId, doAutoRestore);
+        }
+    }
+
+    @Override
+    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+        setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
+    }
+
+    /** Enable/disable automatic restore of app data at install time. */
+    public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.setAutoRestore(autoRestore);
+        }
+    }
+
+    @Override
+    public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
+        return isUserReadyForBackup(userId) && isBackupEnabled(userId);
+    }
+
+    @Override
+    public boolean isBackupEnabled() throws RemoteException {
+        return isBackupEnabledForUser(binderGetCallingUserId());
+    }
+
+    /**
+     * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
+     */
+    public boolean isBackupEnabled(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
+
+        return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
+    }
+
+    /** Sets the backup password used when running adb backup. */
+    @Override
+    public boolean setBackupPassword(String currentPassword, String newPassword) {
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            return false;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        UserHandle.USER_SYSTEM, "setBackupPassword()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
+    }
+
+    /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+    @Override
+    public boolean hasBackupPassword() throws RemoteException {
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            return false;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(
+                        UserHandle.USER_SYSTEM, "hasBackupPassword()");
+
+        return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
+    }
+
+    @Override
+    public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            backupNow(userId);
+        }
+    }
+
+    @Override
+    public void backupNow() throws RemoteException {
+        backupNowForUser(binderGetCallingUserId());
+    }
+
+    /**
+     * Run a backup pass immediately for any key-value backup applications that have declared that
+     * they have pending updates.
+     */
+    public void backupNow(@UserIdInt int userId) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "backupNow()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.backupNow();
+        }
+    }
+
+    /**
+     * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
+     * command line, writing the resulting data stream to the supplied {@code fd}. This method is
+     * synchronous and does not return to the caller until the backup has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    @Override
+    public void adbBackup(
+            @UserIdInt int userId,
+            ParcelFileDescriptor fd,
+            boolean includeApks,
+            boolean includeObbs,
+            boolean includeShared,
+            boolean doWidgets,
+            boolean doAllApps,
+            boolean includeSystem,
+            boolean doCompress,
+            boolean doKeyValue,
+            String[] packageNames) {
+        if (!isUserReadyForBackup(userId)) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbBackup(
+                    fd,
+                    includeApks,
+                    includeObbs,
+                    includeShared,
+                    doWidgets,
+                    doAllApps,
+                    includeSystem,
+                    doCompress,
+                    doKeyValue,
+                    packageNames);
+        }
+    }
+
+    @Override
+    public void fullTransportBackupForUser(int userId, String[] packageNames)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            fullTransportBackup(userId, packageNames);
+        }
+    }
+
+    /**
+     * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+     */
+    public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.fullTransportBackup(packageNames);
+        }
+    }
+
+    /**
+     * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
+     * is synchronous and does not return to the caller until the restore has been completed. It
+     * requires on-screen confirmation by the user.
+     */
+    @Override
+    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
+        if (!isUserReadyForBackup(userId)) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.adbRestore(fd);
+        }
+    }
+
+    @Override
+    public void acknowledgeFullBackupOrRestoreForUser(
+            int userId,
+            int token,
+            boolean allow,
+            String curPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer)
+            throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            acknowledgeAdbBackupOrRestore(userId, token, allow,
+                    curPassword, encryptionPassword, observer);
+        }
+    }
+
+    /**
+     * Confirm that the previously requested adb backup/restore operation can proceed. This is used
+     * to require a user-facing disclosure about the operation.
+     */
+    public void acknowledgeAdbBackupOrRestore(
+            @UserIdInt int userId,
+            int token,
+            boolean allow,
+            String currentPassword,
+            String encryptionPassword,
+            IFullBackupRestoreObserver observer) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.acknowledgeAdbBackupOrRestore(
+                    token, allow, currentPassword, encryptionPassword, observer);
+        }
+    }
+
+    @Override
+    public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+            String encryptionPassword, IFullBackupRestoreObserver observer)
+            throws RemoteException {
+        acknowledgeFullBackupOrRestoreForUser(
+                binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+    }
+
+
+    @Override
+    public String getCurrentTransportForUser(int userId) throws RemoteException {
+        return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null;
+    }
+
+    @Override
+    public String getCurrentTransport() throws RemoteException {
+        return getCurrentTransportForUser(binderGetCallingUserId());
+    }
+
     /** Return the name of the currently active transport. */
     @Nullable
     public String getCurrentTransport(@UserIdInt int userId) {
@@ -285,6 +922,16 @@
     }
 
     /**
+     * Returns the {@link ComponentName} of the host service of the selected transport or
+     * {@code null} if no transport selected or if the transport selected is not registered.
+     */
+    @Override
+    @Nullable
+    public ComponentName getCurrentTransportComponentForUser(int userId) {
+        return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null;
+    }
+
+    /**
      * Returns the {@link ComponentName} of the host service of the selected transport or {@code
      * null} if no transport selected or if the transport selected is not registered.
      */
@@ -298,6 +945,11 @@
                 : userBackupManagerService.getCurrentTransportComponent();
     }
 
+    @Override
+    public String[] listAllTransportsForUser(int userId) throws RemoteException {
+        return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null;
+    }
+
     /** Report all known, available backup transports by name. */
     @Nullable
     public String[] listAllTransports(@UserIdInt int userId) {
@@ -309,6 +961,17 @@
                 : userBackupManagerService.listAllTransports();
     }
 
+    @Override
+    public String[] listAllTransports() throws RemoteException {
+        return listAllTransportsForUser(binderGetCallingUserId());
+    }
+
+    @Override
+    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
+        return (isUserReadyForBackup(userId))
+                ? listAllTransportComponents(userId) : null;
+    }
+
     /** Report all known, available backup transports by {@link ComponentName}. */
     @Nullable
     public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
@@ -320,6 +983,43 @@
                 : userBackupManagerService.listAllTransportComponents();
     }
 
+    @Override
+    public String[] getTransportWhitelist() {
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            return null;
+        }
+        // No permission check, intentionally.
+        String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+        int i = 0;
+        for (ComponentName component : mTransportWhitelist) {
+            whitelistedTransports[i] = component.flattenToShortString();
+            i++;
+        }
+        return whitelistedTransports;
+    }
+
+    @Override
+    public void updateTransportAttributesForUser(
+            int userId,
+            ComponentName transportComponent,
+            String name,
+            @Nullable Intent configurationIntent,
+            String currentDestinationString,
+            @Nullable Intent dataManagementIntent,
+            CharSequence dataManagementLabel) {
+        if (isUserReadyForBackup(userId)) {
+            updateTransportAttributes(
+                    userId,
+                    transportComponent,
+                    name,
+                    configurationIntent,
+                    currentDestinationString,
+                    dataManagementIntent,
+                    dataManagementLabel);
+        }
+    }
+
     /**
      * Update the attributes of the transport identified by {@code transportComponent}. If the
      * specified transport has not been bound at least once (for registration), this call will be
@@ -365,6 +1065,18 @@
         }
     }
 
+    @Override
+    public String selectBackupTransportForUser(int userId, String transport)
+            throws RemoteException {
+        return (isUserReadyForBackup(userId))
+                ? selectBackupTransport(userId, transport) : null;
+    }
+
+    @Override
+    public String selectBackupTransport(String transport) throws RemoteException {
+        return selectBackupTransportForUser(binderGetCallingUserId(), transport);
+    }
+
     /**
      * Selects transport {@code transportName} and returns the previously selected transport.
      *
@@ -382,6 +1094,22 @@
                 : userBackupManagerService.selectBackupTransport(transportName);
     }
 
+    @Override
+    public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
+            ISelectBackupTransportCallback listener) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            selectBackupTransportAsync(userId, transport, listener);
+        } else {
+            if (listener != null) {
+                try {
+                    listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+                } catch (RemoteException ex) {
+                    // ignore
+                }
+            }
+        }
+    }
+
     /**
      * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
      * with the result upon completion.
@@ -398,6 +1126,19 @@
         }
     }
 
+    @Override
+    public Intent getConfigurationIntentForUser(int userId, String transport)
+            throws RemoteException {
+        return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport)
+                : null;
+    }
+
+    @Override
+    public Intent getConfigurationIntent(String transport)
+            throws RemoteException {
+        return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+    }
+
     /**
      * Supply the configuration intent for the given transport. If the name is not one of the
      * available transports, or if the transport does not supply any configuration UI, the method
@@ -409,59 +1150,19 @@
                 getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
 
         return userBackupManagerService == null
-            ? null
-            : userBackupManagerService.getConfigurationIntent(transportName);
+                ? null
+                : userBackupManagerService.getConfigurationIntent(transportName);
     }
 
-    /**
-     * Sets the ancestral work profile for the calling user.
-     *
-     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
-     * callers profile.
-     */
-    public void setAncestralSerialNumber(long ancestralSerialNumber) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(
-                        Binder.getCallingUserHandle().getIdentifier(),
-                        "setAncestralSerialNumber()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
-        }
+    @Override
+    public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
+        return isUserReadyForBackup(userId) ? getDestinationString(userId, transport)
+                : null;
     }
 
-    /**
-     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
-     * serial number of the its ancestral work profile or null if there is no {@link
-     * UserBackupManagerService} associated with that user.
-     *
-     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
-     * and it corresponds to the profile that was used to restore to the callers profile.
-     */
-    @Nullable
-    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
-        int callingUserId = Binder.getCallingUserHandle().getIdentifier();
-        long oldId = Binder.clearCallingIdentity();
-        final int[] userIds;
-        try {
-            userIds =
-                    mContext
-                            .getSystemService(UserManager.class)
-                            .getProfileIds(callingUserId, false);
-        } finally {
-            Binder.restoreCallingIdentity(oldId);
-        }
-
-        for (int userId : userIds) {
-            UserBackupManagerService userBackupManagerService = getUserServices().get(userId);
-            if (userBackupManagerService != null) {
-                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
-                    return UserHandle.of(userId);
-                }
-            }
-        }
-
-        return null;
+    @Override
+    public String getDestinationString(String transport) throws RemoteException {
+        return getDestinationStringForUser(binderGetCallingUserId(), transport);
     }
 
     /**
@@ -483,6 +1184,19 @@
                 : userBackupManagerService.getDestinationString(transportName);
     }
 
+    @Override
+    public Intent getDataManagementIntentForUser(int userId, String transport)
+            throws RemoteException {
+        return isUserReadyForBackup(userId)
+                ? getDataManagementIntent(userId, transport) : null;
+    }
+
+    @Override
+    public Intent getDataManagementIntent(String transport)
+            throws RemoteException {
+        return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+    }
+
     /** Supply the manage-data intent for the given transport. */
     @Nullable
     public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
@@ -494,6 +1208,13 @@
                 : userBackupManagerService.getDataManagementIntent(transportName);
     }
 
+    @Override
+    public CharSequence getDataManagementLabelForUser(int userId, String transport)
+            throws RemoteException {
+        return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport)
+                : null;
+    }
+
     /**
      * Supply the menu label for affordances that fire the manage-data intent for the given
      * transport.
@@ -508,43 +1229,76 @@
                 : userBackupManagerService.getDataManagementLabel(transportName);
     }
 
-    // ---------------------------------------------
-    // SETTINGS OPERATIONS
-    // ---------------------------------------------
-
-    /** Enable/disable the backup service. This is user-configurable via backup settings. */
-    public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "setBackupEnabled()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.setBackupEnabled(enable);
-        }
-    }
-
-    /** Enable/disable automatic restore of app data at install time. */
-    public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "setAutoRestore()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.setAutoRestore(autoRestore);
-        }
+    @Override
+    public IRestoreSession beginRestoreSessionForUser(
+            int userId, String packageName, String transportID) throws RemoteException {
+        return isUserReadyForBackup(userId)
+                ? beginRestoreSession(userId, packageName, transportID) : null;
     }
 
     /**
-     * Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
+     * Begin a restore for the specified package {@code packageName} using the specified transport
+     * {@code transportName}.
      */
-    public boolean isBackupEnabled(@UserIdInt int userId) {
+    @Nullable
+    public IRestoreSession beginRestoreSession(
+            @UserIdInt int userId, String packageName, String transportName) {
         UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "isBackupEnabled()");
+                getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
 
-        return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
+        return userBackupManagerService == null
+                ? null
+                : userBackupManagerService.beginRestoreSession(packageName, transportName);
     }
 
-    // ---------------------------------------------
-    // BACKUP OPERATIONS
-    // ---------------------------------------------
+    @Override
+    public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            opComplete(userId, token, result);
+        }
+    }
+
+    @Override
+    public void opComplete(int token, long result) throws RemoteException {
+        opCompleteForUser(binderGetCallingUserId(), token, result);
+    }
+
+    /**
+     * Used by a currently-active backup agent to notify the service that it has completed its given
+     * outstanding asynchronous backup/restore operation.
+     */
+    public void opComplete(@UserIdInt int userId, int token, long result) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "opComplete()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.opComplete(token, result);
+        }
+    }
+
+    @Override
+    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
+        return isUserReadyForBackup(userId) ? getAvailableRestoreToken(userId, packageName) : 0;
+    }
+
+    /**
+     * Get the restore-set token for the best-available restore set for this {@code packageName}:
+     * the active set if possible, else the ancestral one. Returns zero if none available.
+     */
+    public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
+
+        return userBackupManagerService == null
+                ? 0
+                : userBackupManagerService.getAvailableRestoreToken(packageName);
+    }
+
+    @Override
+    public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
+        return isUserReadyForBackup(userId) && isAppEligibleForBackup(userId,
+                packageName);
+    }
 
     /** Checks if the given package {@code packageName} is eligible for backup. */
     public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
@@ -555,6 +1309,11 @@
                 && userBackupManagerService.isAppEligibleForBackup(packageName);
     }
 
+    @Override
+    public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
+        return isUserReadyForBackup(userId) ? filterAppsEligibleForBackup(userId, packages) : null;
+    }
+
     /**
      * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
      */
@@ -568,17 +1327,20 @@
                 : userBackupManagerService.filterAppsEligibleForBackup(packages);
     }
 
-    /**
-     * Run a backup pass immediately for any key-value backup applications that have declared that
-     * they have pending updates.
-     */
-    public void backupNow(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "backupNow()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.backupNow();
+    @Override
+    public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
+            observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
+        if (!isUserReadyForBackup(userId)) {
+            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
         }
+        return requestBackup(userId, packages, observer, monitor, flags);
+    }
+
+    @Override
+    public int requestBackup(String[] packages, IBackupObserver observer,
+            IBackupManagerMonitor monitor, int flags) throws RemoteException {
+        return requestBackupForUser(binderGetCallingUserId(), packages,
+                observer, monitor, flags);
     }
 
     /**
@@ -599,6 +1361,18 @@
                 : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
     }
 
+    @Override
+    public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
+        if (isUserReadyForBackup(userId)) {
+            cancelBackups(userId);
+        }
+    }
+
+    @Override
+    public void cancelBackups() throws RemoteException {
+        cancelBackupsForUser(binderGetCallingUserId());
+    }
+
     /** Cancel all running backup operations. */
     public void cancelBackups(@UserIdInt int userId) {
         UserBackupManagerService userBackupManagerService =
@@ -610,200 +1384,81 @@
     }
 
     /**
-     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
-     * use is to perform one app backup per scheduled job execution, and to reschedule the job with
-     * zero latency as long as conditions remain right and we still have work to do.
+     * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+     * serial number of its ancestral work profile or null if there is no {@link
+     * UserBackupManagerService} associated with that user.
      *
-     * @return Whether ongoing work will continue. The return value here will be passed along as the
-     *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
+     * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+     * and it corresponds to the profile that was used to restore to the callers profile.
      */
-    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
-
-        return userBackupManagerService != null
-                && userBackupManagerService.beginFullBackup(scheduledJob);
-    }
-
-    /**
-     * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
-     * longer met for running the full backup job.
-     */
-    public void endFullBackup(@UserIdInt int userId) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.endFullBackup();
-        }
-    }
-
-    /**
-     * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
-     */
-    public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.fullTransportBackup(packageNames);
-        }
-    }
-
-    // ---------------------------------------------
-    // RESTORE OPERATIONS
-    // ---------------------------------------------
-
-    /**
-     * Used to run a restore pass for an application that is being installed. This should only be
-     * called from the {@link PackageManager}.
-     */
-    public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.restoreAtInstall(packageName, token);
-        }
-    }
-
-    /**
-     * Begin a restore for the specified package {@code packageName} using the specified transport
-     * {@code transportName}.
-     */
+    @Override
     @Nullable
-    public IRestoreSession beginRestoreSession(
-            @UserIdInt int userId, String packageName, String transportName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
+    public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+        if (mGlobalDisable) {
+            return null;
+        }
+        int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+        long oldId = Binder.clearCallingIdentity();
+        final int[] userIds;
+        try {
+            userIds =
+                    mContext
+                            .getSystemService(UserManager.class)
+                            .getProfileIds(callingUserId, false);
+        } finally {
+            Binder.restoreCallingIdentity(oldId);
+        }
 
-        return userBackupManagerService == null
-                ? null
-                : userBackupManagerService.beginRestoreSession(packageName, transportName);
+        for (int userId : userIds) {
+            UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
+            if (userBackupManagerService != null) {
+                if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
+                    return UserHandle.of(userId);
+                }
+            }
+        }
+
+        return null;
     }
 
     /**
-     * Get the restore-set token for the best-available restore set for this {@code packageName}:
-     * the active set if possible, else the ancestral one. Returns zero if none available.
+     * Sets the ancestral work profile for the calling user.
+     *
+     * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+     * callers profile.
      */
-    public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
-
-        return userBackupManagerService == null
-                ? 0
-                : userBackupManagerService.getAvailableRestoreToken(packageName);
-    }
-
-    // ---------------------------------------------
-    // ADB BACKUP/RESTORE OPERATIONS
-    // ---------------------------------------------
-
-    /** Sets the backup password used when running adb backup. */
-    public boolean setBackupPassword(String currentPassword, String newPassword) {
+    @Override
+    public void setAncestralSerialNumber(long ancestralSerialNumber) {
+        if (mGlobalDisable) {
+            return;
+        }
         UserBackupManagerService userBackupManagerService =
                 getServiceForUserIfCallerHasPermission(
-                        UserHandle.USER_SYSTEM, "setBackupPassword()");
-
-        return userBackupManagerService != null
-                && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
-    }
-
-    /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
-    public boolean hasBackupPassword() {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(
-                        UserHandle.USER_SYSTEM, "hasBackupPassword()");
-
-        return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
-    }
-
-    /**
-     * Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
-     * command line, writing the resulting data stream to the supplied {@code fd}. This method is
-     * synchronous and does not return to the caller until the backup has been completed. It
-     * requires on-screen confirmation by the user.
-     */
-    public void adbBackup(
-            @UserIdInt int userId,
-            ParcelFileDescriptor fd,
-            boolean includeApks,
-            boolean includeObbs,
-            boolean includeShared,
-            boolean doWidgets,
-            boolean doAllApps,
-            boolean includeSystem,
-            boolean doCompress,
-            boolean doKeyValue,
-            String[] packageNames) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
+                        Binder.getCallingUserHandle().getIdentifier(),
+                        "setAncestralSerialNumber()");
 
         if (userBackupManagerService != null) {
-            userBackupManagerService.adbBackup(
-                    fd,
-                    includeApks,
-                    includeObbs,
-                    includeShared,
-                    doWidgets,
-                    doAllApps,
-                    includeSystem,
-                    doCompress,
-                    doKeyValue,
-                    packageNames);
+            userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
         }
     }
 
-    /**
-     * Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
-     * is synchronous and does not return to the caller until the restore has been completed. It
-     * requires on-screen confirmation by the user.
-     */
-    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.adbRestore(fd);
-        }
-    }
-
-    /**
-     * Confirm that the previously requested adb backup/restore operation can proceed. This is used
-     * to require a user-facing disclosure about the operation.
-     */
-    public void acknowledgeAdbBackupOrRestore(
-            @UserIdInt int userId,
-            int token,
-            boolean allow,
-            String currentPassword,
-            String encryptionPassword,
-            IFullBackupRestoreObserver observer) {
-        UserBackupManagerService userBackupManagerService =
-                getServiceForUserIfCallerHasPermission(userId, "acknowledgeAdbBackupOrRestore()");
-
-        if (userBackupManagerService != null) {
-            userBackupManagerService.acknowledgeAdbBackupOrRestore(
-                    token, allow, currentPassword, encryptionPassword, observer);
-        }
-    }
-
-    // ---------------------------------------------
-    //  SERVICE OPERATIONS
-    // ---------------------------------------------
-
-    /** Prints service state for 'dumpsys backup'. */
+    @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
             return;
         }
+        int userId = binderGetCallingUserId();
+        if (!isUserReadyForBackup(userId)) {
+            pw.println("Inactive");
+            return;
+        }
 
         if (args != null) {
             for (String arg : args) {
                 if ("users".equals(arg.toLowerCase())) {
                     pw.print(DUMP_RUNNING_USERS_MESSAGE);
-                    for (int i = 0; i < mServiceUsers.size(); i++) {
-                        pw.print(" " + mServiceUsers.keyAt(i));
+                    for (int i = 0; i < mUserServices.size(); i++) {
+                        pw.print(" " + mUserServices.keyAt(i));
                     }
                     pw.println();
                     return;
@@ -819,31 +1474,103 @@
         }
     }
 
+    /**
+     * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+     * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+     * zero latency as long as conditions remain right and we still have work to do.
+     *
+     * @return Whether ongoing work will continue. The return value here will be passed along as the
+     *     return value to the callback {@link JobService#onStartJob(JobParameters)}.
+     */
+    public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
+        if (!isUserReadyForBackup(userId)) {
+            return false;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
+
+        return userBackupManagerService != null
+                && userBackupManagerService.beginFullBackup(scheduledJob);
+    }
+
+    /**
+     * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+     * longer met for running the full backup job.
+     */
+    public void endFullBackup(@UserIdInt int userId) {
+        if (!isUserReadyForBackup(userId)) {
+            return;
+        }
+        UserBackupManagerService userBackupManagerService =
+                getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
+
+        if (userBackupManagerService != null) {
+            userBackupManagerService.endFullBackup();
+        }
+    }
+
+    /**
+     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+     * If the user is not registered with the service (either the user is locked or not eligible for
+     * the backup service) then return {@code null}.
+     *
+     * @param userId The id of the user to retrieve its instance of {@link
+     *     UserBackupManagerService}.
+     * @param caller A {@link String} identifying the caller for logging purposes.
+     * @throws SecurityException if {@code userId} is different from the calling user id and the
+     *     caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     */
+    @Nullable
+    @VisibleForTesting
+    UserBackupManagerService getServiceForUserIfCallerHasPermission(
+            @UserIdInt int userId, String caller) {
+        enforceCallingPermissionOnUserId(userId, caller);
+        UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
+        if (userBackupManagerService == null) {
+            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+        }
+        return userBackupManagerService;
+    }
+
+    /**
+     * If {@code userId} is different from the calling user id, then the caller must hold the
+     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+     *
+     * @param userId User id on which the backup operation is being requested.
+     * @param message A message to include in the exception if it is thrown.
+     */
+    void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
+        if (Binder.getCallingUserHandle().getIdentifier() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+        }
+    }
+
     /** Implementation to receive lifecycle event callbacks for system services. */
     public static class Lifecycle extends SystemService {
         public Lifecycle(Context context) {
-            this(context, new Trampoline(context));
+            this(context, new BackupManagerService(context));
         }
 
         @VisibleForTesting
-        Lifecycle(Context context, Trampoline trampoline) {
+        Lifecycle(Context context, BackupManagerService backupManagerService) {
             super(context);
-            Trampoline.sInstance = trampoline;
+            sInstance = backupManagerService;
         }
 
         @Override
         public void onStart() {
-            publishService(Context.BACKUP_SERVICE, Trampoline.sInstance);
+            publishService(Context.BACKUP_SERVICE, BackupManagerService.sInstance);
         }
 
         @Override
         public void onUnlockUser(int userId) {
-            Trampoline.sInstance.onUnlockUser(userId);
+            sInstance.onUnlockUser(userId);
         }
 
         @Override
         public void onStopUser(int userId) {
-            Trampoline.sInstance.onStopUser(userId);
+            sInstance.onStopUser(userId);
         }
 
         @VisibleForTesting
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 19a8543..0bb25e3 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -91,7 +91,7 @@
             mParamsForUser.put(userId, params);
         }
 
-        Trampoline service = Trampoline.getInstance();
+        BackupManagerService service = BackupManagerService.getInstance();
         return service.beginFullBackup(userId, this);
     }
 
@@ -105,7 +105,7 @@
             }
         }
 
-        Trampoline service = Trampoline.getInstance();
+        BackupManagerService service = BackupManagerService.getInstance();
         service.endFullBackup(userId);
 
         return false;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index 7b5dbd7..058dcae 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -144,7 +144,7 @@
         }
 
         // Time to run a key/value backup!
-        Trampoline service = Trampoline.getInstance();
+        BackupManagerService service = BackupManagerService.getInstance();
         try {
             service.backupNowForUser(userId);
         } catch (RemoteException e) {}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
deleted file mode 100644
index 59d9c0e..0000000
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * Copyright (C) 2014 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.backup;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.backup.BackupManagerService.TAG;
-
-import static java.util.Collections.emptySet;
-
-import android.Manifest;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyManager;
-import android.app.backup.BackupManager;
-import android.app.backup.IBackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.IRestoreSession;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Binder;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemConfig;
-import com.android.server.backup.utils.RandomAccessFileUtils;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Set;
-
-/**
- * A proxy to the {@link BackupManagerService} implementation.
- *
- * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
- * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
- * implementation object on the fly without disturbing binders that have been cached somewhere in
- * the system.
- *
- * <p>Trampoline determines whether the backup service is available. It can be disabled in the
- * following two ways:
- *
- * <ul>
- * <li>Temporary - create the file {@link #BACKUP_SUPPRESS_FILENAME}, or
- * <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
- * </ul>
- *
- * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
- * privileged callers (currently {@link DevicePolicyManager}). This is called on {@link
- * UserHandle#USER_SYSTEM} and disables backup for all users.
- */
-public class Trampoline extends IBackupManager.Stub {
-    /**
-     * Name of file that disables the backup service. If this file exists, then backup is disabled
-     * for all users.
-     */
-    private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
-
-    /**
-     * Name of file for non-system users that enables the backup service for the user. Backup is
-     * disabled by default in non-system users.
-     */
-    private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
-
-    /**
-     * Name of file for non-system users that remembers whether backup was explicitly activated or
-     * deactivated with a call to setBackupServiceActive.
-     */
-    private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
-
-    // Product-level suppression of backup/restore.
-    private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
-
-    private static final String BACKUP_THREAD = "backup";
-
-    static Trampoline sInstance;
-
-    static Trampoline getInstance() {
-        return checkNotNull(sInstance);
-    }
-
-    private final Context mContext;
-    private final UserManager mUserManager;
-
-    private final boolean mGlobalDisable;
-    // Lock to write backup suppress files.
-    // TODD(b/121198006): remove this object and synchronized all methods on "this".
-    private final Object mStateLock = new Object();
-
-    // TODO: This is not marked as final because of test code. Since we'll merge BMS and Trampoline,
-    // it doesn't make sense to refactor for final. It's never null.
-    @VisibleForTesting
-    protected volatile BackupManagerService mService;
-    private final Handler mHandler;
-    private final Set<ComponentName> mTransportWhitelist;
-
-    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
-                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                if (userId > 0) { // for only non system users
-                    mHandler.post(() -> onRemovedNonSystemUser(userId));
-                }
-            }
-        }
-    };
-
-    public Trampoline(Context context) {
-        mContext = context;
-        mGlobalDisable = isBackupDisabled();
-        HandlerThread handlerThread =
-                new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
-        handlerThread.start();
-        mHandler = new Handler(handlerThread.getLooper());
-        mUserManager = UserManager.get(context);
-        mService = new BackupManagerService(mContext, this);
-        Set<ComponentName> transportWhitelist =
-                SystemConfig.getInstance().getBackupTransportWhitelist();
-        mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
-        mContext.registerReceiver(
-                mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
-    }
-
-    // TODO: Remove this when we implement DI by injecting in the construtor.
-    @VisibleForTesting
-    Handler getBackupHandler() {
-        return mHandler;
-    }
-
-    protected boolean isBackupDisabled() {
-        return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
-    }
-
-    protected int binderGetCallingUserId() {
-        return Binder.getCallingUserHandle().getIdentifier();
-    }
-
-    protected int binderGetCallingUid() {
-        return Binder.getCallingUid();
-    }
-
-    /** Stored in the system user's directory. */
-    protected File getSuppressFileForSystemUser() {
-        return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
-                BACKUP_SUPPRESS_FILENAME);
-    }
-
-    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
-    protected File getRememberActivatedFileForNonSystemUser(int userId) {
-        return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
-    }
-
-    /** Stored in the system user's directory and the file is indexed by the user it refers to. */
-    protected File getActivatedFileForNonSystemUser(int userId) {
-        return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
-    }
-
-    /**
-     * Remove backup state for non system {@code userId} when the user is removed from the device.
-     * For non system users, backup state is stored in both the user's own dir and the system dir.
-     * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
-     * the part of the user backup state which is in the system dir also gets removed.
-     */
-    private void onRemovedNonSystemUser(int userId) {
-        Slog.i(TAG, "Removing state for non system user " + userId);
-        File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
-        if (!FileUtils.deleteContentsAndDir(dir)) {
-            Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
-        }
-    }
-
-    // TODO (b/124359804) move to util method in FileUtils
-    private void createFile(File file) throws IOException {
-        if (file.exists()) {
-            return;
-        }
-
-        file.getParentFile().mkdirs();
-        if (!file.createNewFile()) {
-            Slog.w(TAG, "Failed to create file " + file.getPath());
-        }
-    }
-
-    // TODO (b/124359804) move to util method in FileUtils
-    private void deleteFile(File file) {
-        if (!file.exists()) {
-            return;
-        }
-
-        if (!file.delete()) {
-            Slog.w(TAG, "Failed to delete file " + file.getPath());
-        }
-    }
-
-    /**
-     * Deactivates the backup service for user {@code userId}. If this is the system user, it
-     * creates a suppress file which disables backup for all users. If this is a non-system user, it
-     * only deactivates backup for that user by deleting its activate file.
-     */
-    @GuardedBy("mStateLock")
-    private void deactivateBackupForUserLocked(int userId) throws IOException {
-        if (userId == UserHandle.USER_SYSTEM) {
-            createFile(getSuppressFileForSystemUser());
-        } else {
-            deleteFile(getActivatedFileForNonSystemUser(userId));
-        }
-    }
-
-    /**
-     * Enables the backup service for user {@code userId}. If this is the system user, it deletes
-     * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
-     * deleting the suppress file does not automatically enable backup for non-system users, they
-     * need their own activate file in order to participate in the service.
-     */
-    @GuardedBy("mStateLock")
-    private void activateBackupForUserLocked(int userId) throws IOException {
-        if (userId == UserHandle.USER_SYSTEM) {
-            deleteFile(getSuppressFileForSystemUser());
-        } else {
-            createFile(getActivatedFileForNonSystemUser(userId));
-        }
-    }
-
-    // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
-    // it's used in multiple places where I/O waits would cause system lock-ups.
-    private boolean isUserReadyForBackup(int userId) {
-        return mService.isAbleToServeUser(userId);
-    }
-
-    /**
-     * Backup is activated for the system user if the suppress file does not exist. Backup is
-     * activated for non-system users if the suppress file does not exist AND the user's activated
-     * file exists.
-     */
-    private boolean isBackupActivatedForUser(int userId) {
-        if (getSuppressFileForSystemUser().exists()) {
-            return false;
-        }
-
-        return userId == UserHandle.USER_SYSTEM
-                || getActivatedFileForNonSystemUser(userId).exists();
-    }
-
-    protected Context getContext() {
-        return mContext;
-    }
-
-    protected UserManager getUserManager() {
-        return mUserManager;
-    }
-
-    protected void postToHandler(Runnable runnable) {
-        mHandler.post(runnable);
-    }
-
-    /**
-     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
-     * Starts the backup service for this user if backup is active for this user. Offloads work onto
-     * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
-     * essential for device functioning.
-     */
-    void onUnlockUser(int userId) {
-        postToHandler(() -> startServiceForUser(userId));
-    }
-
-    private void startServiceForUser(int userId) {
-        // We know that the user is unlocked here because it is called from setBackupServiceActive
-        // and unlockUser which have these guarantees. So we can check if the file exists.
-        if (mGlobalDisable) {
-            Slog.i(TAG, "Backup service not supported");
-            return;
-        }
-        if (!isBackupActivatedForUser(userId)) {
-            Slog.i(TAG, "Backup not activated for user " + userId);
-            return;
-        }
-        Slog.i(TAG, "Starting service for user: " + userId);
-        mService.startServiceForUser(userId, mTransportWhitelist);
-    }
-
-    /**
-     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
-     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
-     */
-    void onStopUser(int userId) {
-        postToHandler(
-                () -> {
-                    if (!mGlobalDisable) {
-                        Slog.i(TAG, "Stopping service for user: " + userId);
-                        mService.stopServiceForUser(userId);
-                    }
-                });
-    }
-
-    /** Returns {@link UserBackupManagerService} for user {@code userId}. */
-    @Nullable
-    public UserBackupManagerService getUserService(int userId) {
-        return mService.getUserServices().get(userId);
-    }
-
-    /**
-     * The system user and managed profiles can only be acted on by callers in the system or root
-     * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
-     * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
-     */
-    private void enforcePermissionsOnUser(int userId) throws SecurityException {
-        boolean isRestrictedUser =
-                userId == UserHandle.USER_SYSTEM
-                        || getUserManager().getUserInfo(userId).isManagedProfile();
-
-        if (isRestrictedUser) {
-            int caller = binderGetCallingUid();
-            if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
-                throw new SecurityException("No permission to configure backup activity");
-            }
-        } else {
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.BACKUP, "No permission to configure backup activity");
-            mContext.enforceCallingOrSelfPermission(
-                    Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "No permission to configure backup activity");
-        }
-    }
-
-    /**
-     * Only privileged callers should be changing the backup state. Deactivating backup in the
-     * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
-     * is unlocked at this point yet, so handle both cases.
-     */
-    public void setBackupServiceActive(int userId, boolean makeActive) {
-        enforcePermissionsOnUser(userId);
-
-        // In Q, backup is OFF by default for non-system users. In the future, we will change that
-        // to ON unless backup was explicitly deactivated with a (permissioned) call to
-        // setBackupServiceActive.
-        // Therefore, remember this for use in the future. Basically the default in the future will
-        // be: rememberFile.exists() ? rememberFile.value() : ON
-        // Note that this has to be done right after the permission checks and before any other
-        // action since we need to remember that a permissioned call was made irrespective of
-        // whether the call changes the state or not.
-        if (userId != UserHandle.USER_SYSTEM) {
-            try {
-                File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
-                createFile(rememberFile);
-                RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
-            } catch (IOException e) {
-                Slog.e(TAG, "Unable to persist backup service activity", e);
-            }
-        }
-
-        if (mGlobalDisable) {
-            Slog.i(TAG, "Backup service not supported");
-            return;
-        }
-
-        synchronized (mStateLock) {
-            Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
-            if (makeActive) {
-                try {
-                    activateBackupForUserLocked(userId);
-                } catch (IOException e) {
-                    Slog.e(TAG, "Unable to persist backup service activity");
-                }
-
-                // If the user is unlocked, we can start the backup service for it. Otherwise we
-                // will start the service when the user is unlocked as part of its unlock callback.
-                if (getUserManager().isUserUnlocked(userId)) {
-                    // Clear calling identity as initialization enforces the system identity but we
-                    // can be coming from shell.
-                    long oldId = Binder.clearCallingIdentity();
-                    try {
-                        startServiceForUser(userId);
-                    } finally {
-                        Binder.restoreCallingIdentity(oldId);
-                    }
-                }
-            } else {
-                try {
-                    //TODO(b/121198006): what if this throws an exception?
-                    deactivateBackupForUserLocked(userId);
-                } catch (IOException e) {
-                    Slog.e(TAG, "Unable to persist backup service inactivity");
-                }
-                //TODO(b/121198006): loop through active users that have work profile and
-                // stop them as well.
-                onStopUser(userId);
-            }
-        }
-    }
-
-    // IBackupManager binder API
-
-    /**
-     * Querying activity state of backup service.
-     *
-     * @param userId The user in which the activity state of backup service is queried.
-     * @return true if the service is active.
-     */
-    @Override
-    public boolean isBackupServiceActive(int userId) {
-        synchronized (mStateLock) {
-            return !mGlobalDisable && isBackupActivatedForUser(userId);
-        }
-    }
-
-    @Override
-    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.dataChanged(userId, packageName);
-        }
-    }
-
-    @Override
-    public void dataChanged(String packageName) throws RemoteException {
-        dataChangedForUser(binderGetCallingUserId(), packageName);
-    }
-
-    @Override
-    public void initializeTransportsForUser(
-            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.initializeTransports(userId, transportNames, observer);
-        }
-    }
-
-    @Override
-    public void clearBackupDataForUser(int userId, String transportName, String packageName)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.clearBackupData(userId, transportName, packageName);
-        }
-    }
-
-    @Override
-    public void clearBackupData(String transportName, String packageName)
-            throws RemoteException {
-        clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
-    }
-
-    @Override
-    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.agentConnected(userId, packageName, agent);
-        }
-    }
-
-    @Override
-    public void agentConnected(String packageName, IBinder agent) throws RemoteException {
-        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
-    }
-
-    @Override
-    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.agentDisconnected(userId, packageName);
-        }
-    }
-
-    @Override
-    public void agentDisconnected(String packageName) throws RemoteException {
-        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
-    }
-
-    @Override
-    public void restoreAtInstallForUser(int userId, String packageName, int token)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.restoreAtInstall(userId, packageName, token);
-        }
-    }
-
-    @Override
-    public void restoreAtInstall(String packageName, int token) throws RemoteException {
-        restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
-    }
-
-    @Override
-    public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.setBackupEnabled(userId, isEnabled);
-        }
-    }
-
-    @Override
-    public void setBackupEnabled(boolean isEnabled) throws RemoteException {
-        setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
-    }
-
-    @Override
-    public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.setAutoRestore(userId, doAutoRestore);
-        }
-    }
-
-    @Override
-    public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
-        setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
-    }
-
-    @Override
-    public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
-        return isUserReadyForBackup(userId) && mService.isBackupEnabled(userId);
-    }
-
-    @Override
-    public boolean isBackupEnabled() throws RemoteException {
-        return isBackupEnabledForUser(binderGetCallingUserId());
-    }
-
-    @Override
-    public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException {
-        int userId = binderGetCallingUserId();
-        return (isUserReadyForBackup(userId)) && mService.setBackupPassword(currentPw, newPw);
-    }
-
-    @Override
-    public boolean hasBackupPassword() throws RemoteException {
-        int userId = binderGetCallingUserId();
-        return (isUserReadyForBackup(userId)) && mService.hasBackupPassword();
-    }
-
-    @Override
-    public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.backupNow(userId);
-        }
-    }
-
-    @Override
-    public void backupNow() throws RemoteException {
-        backupNowForUser(binderGetCallingUserId());
-    }
-
-    public void adbBackup(@UserIdInt int userId, ParcelFileDescriptor fd,
-            boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
-            boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue,
-            String[] packageNames) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
-                    allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
-        }
-    }
-
-    @Override
-    public void fullTransportBackupForUser(int userId, String[] packageNames)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.fullTransportBackup(userId, packageNames);
-        }
-    }
-
-    @Override
-    public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.adbRestore(userId, fd);
-        }
-    }
-
-    @Override
-    public void acknowledgeFullBackupOrRestoreForUser(
-            int userId,
-            int token,
-            boolean allow,
-            String curPassword,
-            String encryptionPassword,
-            IFullBackupRestoreObserver observer)
-            throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.acknowledgeAdbBackupOrRestore(userId, token, allow,
-                    curPassword, encryptionPassword, observer);
-        }
-    }
-
-    @Override
-    public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
-            String encryptionPassword, IFullBackupRestoreObserver observer)
-            throws RemoteException {
-        acknowledgeFullBackupOrRestoreForUser(
-                binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
-    }
-
-
-    @Override
-    public String getCurrentTransportForUser(int userId) throws RemoteException {
-        return (isUserReadyForBackup(userId)) ? mService.getCurrentTransport(userId) : null;
-    }
-
-    @Override
-    public String getCurrentTransport() throws RemoteException {
-        return getCurrentTransportForUser(binderGetCallingUserId());
-    }
-
-    /**
-     * Returns the {@link ComponentName} of the host service of the selected transport or
-     * {@code null} if no transport selected or if the transport selected is not registered.
-     */
-    @Override
-    @Nullable
-    public ComponentName getCurrentTransportComponentForUser(int userId) {
-        return (isUserReadyForBackup(userId))
-                ? mService.getCurrentTransportComponent(userId) : null;
-    }
-
-    @Override
-    public String[] listAllTransportsForUser(int userId) throws RemoteException {
-        return (isUserReadyForBackup(userId)) ? mService.listAllTransports(userId) : null;
-    }
-
-    @Override
-    public String[] listAllTransports() throws RemoteException {
-        return listAllTransportsForUser(binderGetCallingUserId());
-    }
-
-    @Override
-    public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
-        return (isUserReadyForBackup(userId))
-                ? mService.listAllTransportComponents(userId) : null;
-    }
-
-    @Override
-    public String[] getTransportWhitelist() {
-        int userId = binderGetCallingUserId();
-        if (!isUserReadyForBackup(userId)) {
-            return null;
-        }
-        // No permission check, intentionally.
-        String[] whitelistedTransports = new String[mTransportWhitelist.size()];
-        int i = 0;
-        for (ComponentName component : mTransportWhitelist) {
-            whitelistedTransports[i] = component.flattenToShortString();
-            i++;
-        }
-        return whitelistedTransports;
-    }
-
-    @Override
-    public void updateTransportAttributesForUser(
-            int userId,
-            ComponentName transportComponent,
-            String name,
-            @Nullable Intent configurationIntent,
-            String currentDestinationString,
-            @Nullable Intent dataManagementIntent,
-            CharSequence dataManagementLabel) {
-        if (isUserReadyForBackup(userId)) {
-            mService.updateTransportAttributes(
-                    userId,
-                    transportComponent,
-                    name,
-                    configurationIntent,
-                    currentDestinationString,
-                    dataManagementIntent,
-                    dataManagementLabel);
-        }
-    }
-
-    @Override
-    public String selectBackupTransportForUser(int userId, String transport)
-            throws RemoteException {
-        return (isUserReadyForBackup(userId))
-                ? mService.selectBackupTransport(userId, transport) : null;
-    }
-
-    @Override
-    public String selectBackupTransport(String transport) throws RemoteException {
-        return selectBackupTransportForUser(binderGetCallingUserId(), transport);
-    }
-
-    @Override
-    public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
-            ISelectBackupTransportCallback listener) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.selectBackupTransportAsync(userId, transport, listener);
-        } else {
-            if (listener != null) {
-                try {
-                    listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
-                } catch (RemoteException ex) {
-                    // ignore
-                }
-            }
-        }
-    }
-
-    @Override
-    public Intent getConfigurationIntentForUser(int userId, String transport)
-            throws RemoteException {
-        return isUserReadyForBackup(userId) ? mService.getConfigurationIntent(userId, transport)
-                : null;
-    }
-
-    @Override
-    public Intent getConfigurationIntent(String transport)
-            throws RemoteException {
-        return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
-    }
-
-    @Override
-    public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
-        return isUserReadyForBackup(userId) ? mService.getDestinationString(userId, transport)
-                : null;
-    }
-
-    @Override
-    public String getDestinationString(String transport) throws RemoteException {
-        return getDestinationStringForUser(binderGetCallingUserId(), transport);
-    }
-
-    @Override
-    public Intent getDataManagementIntentForUser(int userId, String transport)
-            throws RemoteException {
-        return isUserReadyForBackup(userId)
-                ? mService.getDataManagementIntent(userId, transport) : null;
-    }
-
-    @Override
-    public Intent getDataManagementIntent(String transport)
-            throws RemoteException {
-        return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
-    }
-
-    @Override
-    public CharSequence getDataManagementLabelForUser(int userId, String transport)
-            throws RemoteException {
-        return isUserReadyForBackup(userId) ? mService.getDataManagementLabel(userId, transport)
-                : null;
-    }
-
-    @Override
-    public IRestoreSession beginRestoreSessionForUser(
-            int userId, String packageName, String transportID) throws RemoteException {
-        return isUserReadyForBackup(userId) ? mService.beginRestoreSession(userId, packageName,
-                transportID) : null;
-    }
-
-    @Override
-    public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.opComplete(userId, token, result);
-        }
-    }
-
-    @Override
-    public void opComplete(int token, long result) throws RemoteException {
-        opCompleteForUser(binderGetCallingUserId(), token, result);
-    }
-
-    @Override
-    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
-        return isUserReadyForBackup(userId) ? mService.getAvailableRestoreToken(userId,
-                packageName) : 0;
-    }
-
-    @Override
-    public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
-        return isUserReadyForBackup(userId) && mService.isAppEligibleForBackup(userId,
-                packageName);
-    }
-
-    @Override
-    public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
-        return isUserReadyForBackup(userId) ? mService.filterAppsEligibleForBackup(userId,
-                packages) : null;
-    }
-
-    @Override
-    public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
-            observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
-        if (!isUserReadyForBackup(userId)) {
-            return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
-        }
-        return mService.requestBackup(userId, packages, observer, monitor, flags);
-    }
-
-    @Override
-    public int requestBackup(String[] packages, IBackupObserver observer,
-            IBackupManagerMonitor monitor, int flags) throws RemoteException {
-        return requestBackupForUser(binderGetCallingUserId(), packages,
-                observer, monitor, flags);
-    }
-
-    @Override
-    public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
-        if (isUserReadyForBackup(userId)) {
-            mService.cancelBackups(userId);
-        }
-    }
-
-    @Override
-    public void cancelBackups() throws RemoteException {
-        cancelBackupsForUser(binderGetCallingUserId());
-    }
-
-    @Override
-    @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
-        if (mGlobalDisable) {
-            return null;
-        }
-        return mService.getUserForAncestralSerialNumber(ancestralSerialNumber);
-    }
-
-    @Override
-    public void setAncestralSerialNumber(long ancestralSerialNumber) {
-        if (!mGlobalDisable) {
-            mService.setAncestralSerialNumber(ancestralSerialNumber);
-        }
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
-        int userId = binderGetCallingUserId();
-        if (isUserReadyForBackup(userId)) {
-            mService.dump(fd, pw, args);
-        } else {
-            pw.println("Inactive");
-        }
-    }
-
-    // Full backup/restore entry points - non-Binder; called directly
-    // by the full-backup scheduled job
-    /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
-        return (isUserReadyForBackup(userId)) && mService.beginFullBackup(userId, scheduledJob);
-    }
-
-    /* package */ void endFullBackup(@UserIdInt int userId) {
-        if (isUserReadyForBackup(userId)) {
-            mService.endFullBackup(userId);
-        }
-    }
-}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index d599aab..77888db 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -421,13 +421,13 @@
      * Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
      * includes setting up the directories where we keep our bookkeeping and transport management.
      *
-     * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
-     *     TransportManager)
+     * @see #createAndInitializeService(int, Context, BackupManagerService, HandlerThread, File,
+     * File, TransportManager)
      */
     static UserBackupManagerService createAndInitializeService(
             @UserIdInt int userId,
             Context context,
-            Trampoline trampoline,
+            BackupManagerService backupManagerService,
             Set<ComponentName> transportWhitelist) {
         String currentTransport =
                 Settings.Secure.getStringForUser(
@@ -455,7 +455,7 @@
         return createAndInitializeService(
                 userId,
                 context,
-                trampoline,
+                backupManagerService,
                 userBackupThread,
                 baseStateDir,
                 dataDir,
@@ -467,7 +467,7 @@
      *
      * @param userId The user which this service is for.
      * @param context The system server context.
-     * @param trampoline A reference to the proxy to {@link BackupManagerService}.
+     * @param backupManagerService A reference to the proxy to {@link BackupManagerService}.
      * @param userBackupThread The thread running backup/restore operations for the user.
      * @param baseStateDir The directory we store the user's persistent bookkeeping data.
      * @param dataDir The directory we store the user's temporary staging data.
@@ -478,7 +478,7 @@
     public static UserBackupManagerService createAndInitializeService(
             @UserIdInt int userId,
             Context context,
-            Trampoline trampoline,
+            BackupManagerService backupManagerService,
             HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
@@ -486,7 +486,7 @@
         return new UserBackupManagerService(
                 userId,
                 context,
-                trampoline,
+                backupManagerService,
                 userBackupThread,
                 baseStateDir,
                 dataDir,
@@ -509,7 +509,7 @@
     private UserBackupManagerService(
             @UserIdInt int userId,
             Context context,
-            Trampoline parent,
+            BackupManagerService parent,
             HandlerThread userBackupThread,
             File baseStateDir,
             File dataDir,
@@ -525,8 +525,8 @@
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
 
-        checkNotNull(parent, "trampoline cannot be null");
-        mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+        checkNotNull(parent, "parent cannot be null");
+        mBackupManagerBinder = BackupManagerService.asInterface(parent.asBinder());
 
         mAgentTimeoutParameters = new
                 BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
@@ -649,7 +649,8 @@
     }
 
     /** Cleans up state when the user of this service is stopped. */
-    void tearDownService() {
+    @VisibleForTesting
+    protected void tearDownService() {
         mAgentTimeoutParameters.stop();
         mConstants.stop();
         mContext.getContentResolver().unregisterContentObserver(mSetupObserver);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 474dbfe..c838c60 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -13,7 +13,6 @@
     },
     srcs: [
         "java/**/*.java",
-        ":platformcompat_aidl",
         ":dumpstate_aidl",
         ":idmap2_aidl",
         ":installd_aidl",
@@ -82,11 +81,3 @@
     name: "gps_debug.conf",
     src: "java/com/android/server/location/gps_debug.conf",
 }
-
-filegroup {
-    name: "platformcompat_aidl",
-    srcs: [
-        "java/com/android/server/compat/IPlatformCompat.aidl",
-    ],
-    path: "java",
-}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index fede487..9a97ddb 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -172,7 +172,7 @@
     final LocalLog mLog = new LocalLog(TAG);
 
     AppOpsManager mAppOps;
-    DeviceIdleController.LocalService mLocalDeviceIdleController;
+    DeviceIdleInternal mLocalDeviceIdleController;
     private UsageStatsManagerInternal mUsageStatsManagerInternal;
 
     final Object mLock = new Object();
@@ -1594,7 +1594,7 @@
                 mConstants.start(getContext().getContentResolver());
                 mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
                 mLocalDeviceIdleController =
-                        LocalServices.getService(DeviceIdleController.LocalService.class);
+                        LocalServices.getService(DeviceIdleInternal.class);
                 mUsageStatsManagerInternal =
                         LocalServices.getService(UsageStatsManagerInternal.class);
                 mUsageStatsManagerInternal.addAppIdleStateChangeListener(new AppStandbyTracker());
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
index c86042b..c607b1e 100644
--- a/services/core/java/com/android/server/AnimationThread.java
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -21,6 +21,8 @@
 import android.os.Handler;
 import android.os.Trace;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Thread for handling all legacy window animations, or anything that's directly impacting
  * animations like starting windows or traversals.
@@ -55,4 +57,20 @@
             return sHandler;
         }
     }
+
+    /**
+     * Disposes current animation thread if it's initialized. Should only be used in tests to set up
+     * a new environment.
+     */
+    @VisibleForTesting
+    public static void dispose() {
+        synchronized (DisplayThread.class) {
+            if (sInstance == null) {
+                return;
+            }
+
+            getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+            sInstance = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
index 112cf08..0bcd937 100644
--- a/services/core/java/com/android/server/BluetoothService.java
+++ b/services/core/java/com/android/server/BluetoothService.java
@@ -53,8 +53,11 @@
 
     @Override
     public void onSwitchUser(int userHandle) {
-        initialize();
-        mBluetoothManagerService.handleOnSwitchUser(userHandle);
+        if (!mInitialized) {
+            initialize();
+        } else {
+            mBluetoothManagerService.handleOnSwitchUser(userHandle);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index a303718..62930b0 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -1636,27 +1636,32 @@
         }
     }
 
-    public class LocalService {
+    private class LocalService implements DeviceIdleInternal {
+        @Override
         public void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active) {
             synchronized (DeviceIdleController.this) {
                 onConstraintStateChangedLocked(constraint, active);
             }
         }
 
+        @Override
         public void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name,
                 @IDeviceIdleConstraint.MinimumState int minState) {
             registerDeviceIdleConstraintInternal(constraint, name, minState);
         }
 
+        @Override
         public void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint) {
             unregisterDeviceIdleConstraintInternal(constraint);
         }
 
+        @Override
         public void exitIdle(String reason) {
             exitIdleInternal(reason);
         }
 
         // duration in milliseconds
+        @Override
         public void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
                 long duration, int userId, boolean sync, String reason) {
             addPowerSaveTempWhitelistAppInternal(callingUid, packageName, duration,
@@ -1664,26 +1669,31 @@
         }
 
         // duration in milliseconds
+        @Override
         public void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync,
                 String reason) {
             addPowerSaveTempWhitelistAppDirectInternal(0, uid, duration, sync, reason);
         }
 
         // duration in milliseconds
+        @Override
         public long getNotificationWhitelistDuration() {
             return mConstants.NOTIFICATION_WHITELIST_DURATION;
         }
 
+        @Override
         public void setJobsActive(boolean active) {
             DeviceIdleController.this.setJobsActive(active);
         }
 
         // Up-call from alarm manager.
+        @Override
         public void setAlarmsActive(boolean active) {
             DeviceIdleController.this.setAlarmsActive(active);
         }
 
         /** Is the app on any of the power save whitelists, whether system or user? */
+        @Override
         public boolean isAppOnWhitelist(int appid) {
             return DeviceIdleController.this.isAppOnWhitelistInternal(appid);
         }
@@ -1694,10 +1704,12 @@
          * can change when the list changes, so it needs to be re-acquired when
          * {@link PowerManager#ACTION_POWER_SAVE_WHITELIST_CHANGED} is sent.
          */
+        @Override
         public int[] getPowerSaveWhitelistUserAppIds() {
             return DeviceIdleController.this.getPowerSaveWhitelistUserAppIds();
         }
 
+        @Override
         public int[] getPowerSaveTempWhitelistAppIds() {
             return DeviceIdleController.this.getAppIdTempWhitelistInternal();
         }
@@ -1767,7 +1779,8 @@
             return mContext.getSystemService(SensorManager.class);
         }
 
-        ConstraintController getConstraintController(Handler handler, LocalService localService) {
+        ConstraintController getConstraintController(Handler handler,
+                DeviceIdleInternal localService) {
             if (mContext.getPackageManager()
                     .hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)) {
                 return new TvConstraintController(mContext, handler);
@@ -1884,7 +1897,7 @@
 
         mBinderService = new BinderService();
         publishBinderService(Context.DEVICE_IDLE_CONTROLLER, mBinderService);
-        publishLocalService(LocalService.class, new LocalService());
+        publishLocalService(DeviceIdleInternal.class, new LocalService());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/DeviceIdleInternal.java b/services/core/java/com/android/server/DeviceIdleInternal.java
new file mode 100644
index 0000000..1273249
--- /dev/null
+++ b/services/core/java/com/android/server/DeviceIdleInternal.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 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;
+
+import com.android.server.deviceidle.IDeviceIdleConstraint;
+
+public interface DeviceIdleInternal {
+    void onConstraintStateChanged(IDeviceIdleConstraint constraint, boolean active);
+
+    void registerDeviceIdleConstraint(IDeviceIdleConstraint constraint, String name,
+            @IDeviceIdleConstraint.MinimumState int minState);
+
+    void unregisterDeviceIdleConstraint(IDeviceIdleConstraint constraint);
+
+    void exitIdle(String reason);
+
+    // duration in milliseconds
+    void addPowerSaveTempWhitelistApp(int callingUid, String packageName,
+            long duration, int userId, boolean sync, String reason);
+
+    // duration in milliseconds
+    void addPowerSaveTempWhitelistAppDirect(int uid, long duration, boolean sync,
+            String reason);
+
+    // duration in milliseconds
+    long getNotificationWhitelistDuration();
+
+    void setJobsActive(boolean active);
+
+    // Up-call from alarm manager.
+    void setAlarmsActive(boolean active);
+
+    boolean isAppOnWhitelist(int appid);
+
+    int[] getPowerSaveWhitelistUserAppIds();
+
+    int[] getPowerSaveTempWhitelistAppIds();
+}
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index 85c799c..a07ade0 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -20,6 +20,8 @@
 import android.os.Process;
 import android.os.Trace;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Shared singleton foreground thread for the system.  This is a thread for
  * operations that affect what's on the display, which needs to have a minimum
@@ -58,4 +60,20 @@
             return sHandler;
         }
     }
+
+    /**
+     * Disposes current display thread if it's initialized. Should only be used in tests to set up a
+     * new environment.
+     */
+    @VisibleForTesting
+    public static void dispose() {
+        synchronized (DisplayThread.class) {
+            if (sInstance == null) {
+                return;
+            }
+
+            getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+            sInstance = null;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
index 4639d75..70569db 100644
--- a/services/core/java/com/android/server/GraphicsStatsService.java
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -191,7 +191,7 @@
             if (!file.getFileDescriptor().valid()) {
                 throw new IllegalStateException("Invalid file descriptor");
             }
-            return new ParcelFileDescriptor(file.getFileDescriptor());
+            return ParcelFileDescriptor.dup(file.getFileDescriptor());
         } catch (IOException ex) {
             throw new IllegalStateException("Failed to get PFD from memory file", ex);
         }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index dee89e5..ea3dd3d 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -25,7 +25,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
-import android.net.NetworkStackClient;
+import android.net.ConnectivityModuleConnector;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Looper;
@@ -116,7 +116,7 @@
     // File containing the XML data of monitored packages /data/system/package-watchdog.xml
     private final AtomicFile mPolicyFile;
     private final ExplicitHealthCheckController mHealthCheckController;
-    private final NetworkStackClient mNetworkStackClient;
+    private final ConnectivityModuleConnector mConnectivityModuleConnector;
     @GuardedBy("mLock")
     private boolean mIsPackagesReady;
     // Flag to control whether explicit health checks are supported or not
@@ -138,7 +138,7 @@
                                 "package-watchdog.xml")),
                 new Handler(Looper.myLooper()), BackgroundThread.getHandler(),
                 new ExplicitHealthCheckController(context),
-                NetworkStackClient.getInstance());
+                ConnectivityModuleConnector.getInstance());
     }
 
     /**
@@ -147,13 +147,13 @@
     @VisibleForTesting
     PackageWatchdog(Context context, AtomicFile policyFile, Handler shortTaskHandler,
             Handler longTaskHandler, ExplicitHealthCheckController controller,
-            NetworkStackClient networkStackClient) {
+            ConnectivityModuleConnector connectivityModuleConnector) {
         mContext = context;
         mPolicyFile = policyFile;
         mShortTaskHandler = shortTaskHandler;
         mLongTaskHandler = longTaskHandler;
         mHealthCheckController = controller;
-        mNetworkStackClient = networkStackClient;
+        mConnectivityModuleConnector = connectivityModuleConnector;
         loadFromFile();
     }
 
@@ -179,7 +179,7 @@
                     () -> syncRequestsAsync());
             setPropertyChangedListenerLocked();
             updateConfigs();
-            registerNetworkStackHealthListener();
+            registerConnectivityModuleHealthListener();
         }
     }
 
@@ -743,11 +743,11 @@
         }
     }
 
-    private void registerNetworkStackHealthListener() {
+    private void registerConnectivityModuleHealthListener() {
         // TODO: have an internal method to trigger a rollback by reporting high severity errors,
         // and rely on ActivityManager to inform the watchdog of severe network stack crashes
         // instead of having this listener in parallel.
-        mNetworkStackClient.registerHealthListener(
+        mConnectivityModuleConnector.registerHealthListener(
                 packageName -> {
                     final VersionedPackage pkg = getVersionedPackage(packageName);
                     if (pkg == null) {
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index 8e9e74e..4facf4ea 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -18,12 +18,17 @@
 
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT;
 
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.ActivityThread;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.IBinder;
 import android.os.ServiceManager;
 import android.os.UserManager;
 
+import com.android.server.pm.UserManagerService;
+
 /**
  * The base class for services running in the system process. Override and implement
  * the lifecycle event callback methods as needed.
@@ -145,11 +150,29 @@
     public void onBootPhase(int phase) {}
 
     /**
+     * @deprecated subclasses should extend {@link #onStartUser(int, int)} instead (which by default
+     * calls this method).
+     */
+    @Deprecated
+    public void onStartUser(@UserIdInt int userHandle) {}
+
+    /**
      * Called when a new user is starting, for system services to initialize any per-user
      * state they maintain for running users.
-     * @param userHandle The identifier of the user.
+     *
+     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+     * referenced by {@link UserManagerService} and hence should not be modified.
      */
-    public void onStartUser(int userHandle) {}
+    public void onStartUser(@NonNull UserInfo userInfo) {
+        onStartUser(userInfo.id);
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onUnlockUser(int, int)} instead (which by
+     * default calls this method).
+     */
+    @Deprecated
+    public void onUnlockUser(@UserIdInt int userHandle) {}
 
     /**
      * Called when an existing user is in the process of being unlocked. This
@@ -163,17 +186,38 @@
      * {@link UserManager#isUserUnlockingOrUnlocked(int)} to handle both of
      * these states.
      *
-     * @param userHandle The identifier of the user.
+     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+     * referenced by {@link UserManagerService} and hence should not be modified.
      */
-    public void onUnlockUser(int userHandle) {}
+    public void onUnlockUser(@NonNull UserInfo userInfo) {
+        onUnlockUser(userInfo.id);
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onSwitchUser(int, int)} instead (which by
+     * default calls this method).
+     */
+    @Deprecated
+    public void onSwitchUser(@UserIdInt int userHandle) {}
 
     /**
      * Called when switching to a different foreground user, for system services that have
      * special behavior for whichever user is currently in the foreground.  This is called
      * before any application processes are aware of the new user.
-     * @param userHandle The identifier of the user.
+     *
+     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+     * referenced by {@link UserManagerService} and hence should not be modified.
      */
-    public void onSwitchUser(int userHandle) {}
+    public void onSwitchUser(@NonNull UserInfo userInfo) {
+        onSwitchUser(userInfo.id);
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onStopUser(int, int)} instead (which by default
+     * calls this method).
+     */
+    @Deprecated
+    public void onStopUser(@UserIdInt int userHandle) {}
 
     /**
      * Called when an existing user is stopping, for system services to finalize any per-user
@@ -183,9 +227,19 @@
      *
      * <p>NOTE: This is the last callback where the callee may access the target user's CE storage.
      *
-     * @param userHandle The identifier of the user.
+     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+     * referenced by {@link UserManagerService} and hence should not be modified.
      */
-    public void onStopUser(int userHandle) {}
+    public void onStopUser(@NonNull UserInfo userInfo) {
+        onStopUser(userInfo.id);
+    }
+
+    /**
+     * @deprecated subclasses should extend {@link #onCleanupUser(int, int)} instead (which by
+     * default calls this method).
+     */
+    @Deprecated
+    public void onCleanupUser(@UserIdInt int userHandle) {}
 
     /**
      * Called when an existing user is stopping, for system services to finalize any per-user
@@ -193,11 +247,14 @@
      * teardown of the user is complete.
      *
      * <p>NOTE: When this callback is called, the CE storage for the target user may not be
-     * accessible already.  Use {@link #onStopUser} instead if you need to access the CE storage.
+     * accessible already.  Use {@link #onCleanupUser} instead if you need to access the CE storage.
      *
-     * @param userHandle The identifier of the user.
+     * @param userInfo The information about the user. <b>NOTE: </b> this is a "live" object
+     * referenced by {@link UserManagerService} and hence should not be modified.
      */
-    public void onCleanupUser(int userHandle) {}
+    public void onCleanupUser(@NonNull UserInfo userInfo) {
+        onCleanupUser(userInfo.id);
+    }
 
     /**
      * Publish the service so it is accessible to other services and apps.
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index 9711152..ebe23f6 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -19,9 +19,11 @@
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.os.Environment;
 import android.os.SystemClock;
 import android.os.Trace;
+import android.os.UserManagerInternal;
 import android.util.Slog;
 
 import com.android.server.utils.TimingsTraceAndSlog;
@@ -53,6 +55,8 @@
 
     private int mCurrentPhase = -1;
 
+    private UserManagerInternal mUserManagerInternal;
+
     SystemServiceManager(Context context) {
         mContext = context;
     }
@@ -187,11 +191,30 @@
     }
 
     /**
+     * Called at the beginning of {@code ActivityManagerService.systemReady()}.
+     */
+    public void preSystemReady() {
+        mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
+    }
+
+    private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) {
+        if (mUserManagerInternal == null) {
+            throw new IllegalStateException("mUserManagerInternal not set yet");
+        }
+        final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle);
+        if (userInfo == null) {
+            throw new IllegalStateException("No UserInfo for " + userHandle);
+        }
+        return userInfo;
+    }
+
+    /**
      * Starts the given user.
      */
-    public void startUser(@NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
+    public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
         t.traceBegin("ssm.startUser-" + userHandle);
         Slog.i(TAG, "Calling onStartUser u" + userHandle);
+        final UserInfo userInfo = getUserInfo(userHandle);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
@@ -199,7 +222,7 @@
             t.traceBegin("onStartUser-" + userHandle + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                service.onStartUser(userHandle);
+                service.onStartUser(userInfo);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting start of user " + userHandle
                         + " to service " + service.getClass().getName(), ex);
@@ -217,6 +240,7 @@
         final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
         t.traceBegin("ssm.unlockUser-" + userHandle);
         Slog.i(TAG, "Calling onUnlockUser u" + userHandle);
+        final UserInfo userInfo = getUserInfo(userHandle);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
@@ -224,7 +248,7 @@
             t.traceBegin("onUnlockUser-" + userHandle + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                service.onUnlockUser(userHandle);
+                service.onUnlockUser(userInfo);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
                         + " to service " + serviceName, ex);
@@ -242,6 +266,7 @@
         final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
         t.traceBegin("ssm.switchUser-" + userHandle);
         Slog.i(TAG, "Calling switchUser u" + userHandle);
+        final UserInfo userInfo = getUserInfo(userHandle);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
@@ -249,7 +274,7 @@
             t.traceBegin("onSwitchUser-" + userHandle + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                service.onSwitchUser(userHandle);
+                service.onSwitchUser(userInfo);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
                         + " to service " + serviceName, ex);
@@ -267,6 +292,7 @@
         final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
         t.traceBegin("ssm.stopUser-" + userHandle);
         Slog.i(TAG, "Calling onStopUser u" + userHandle);
+        final UserInfo userInfo = getUserInfo(userHandle);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
@@ -274,7 +300,7 @@
             t.traceBegin("onStopUser-" + userHandle + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                service.onStopUser(userHandle);
+                service.onStopUser(userInfo);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
                         + " to service " + serviceName, ex);
@@ -292,6 +318,7 @@
         final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
         t.traceBegin("ssm.cleanupUser-" + userHandle);
         Slog.i(TAG, "Calling onCleanupUser u" + userHandle);
+        final UserInfo userInfo = getUserInfo(userHandle);
         final int serviceLen = mServices.size();
         for (int i = 0; i < serviceLen; i++) {
             final SystemService service = mServices.get(i);
@@ -299,7 +326,7 @@
             t.traceBegin("onCleanupUser-" + userHandle + " " + serviceName);
             long time = SystemClock.elapsedRealtime();
             try {
-                service.onCleanupUser(userHandle);
+                service.onCleanupUser(userInfo);
             } catch (Exception ex) {
                 Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
                         + " to service " + serviceName, ex);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ede573a..2292889 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -282,6 +282,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.EventLog;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
@@ -330,7 +331,7 @@
 import com.android.internal.util.function.TriFunction;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.AttributeCache;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.DisplayThread;
 import com.android.server.IntentResolver;
 import com.android.server.IoThread;
@@ -378,7 +379,7 @@
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -437,6 +438,10 @@
     // need not be the case.
     public static final String ACTION_TRIGGER_IDLE = "com.android.server.ACTION_TRIGGER_IDLE";
 
+    private static final String INTENT_BUGREPORT_REQUESTED =
+            "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+    private static final String SHELL_APP_PACKAGE = "com.android.shell";
+
     /** Control over CPU and battery monitoring */
     // write battery stats every 30 minutes.
     static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
@@ -555,6 +560,10 @@
     OomAdjuster mOomAdjuster;
     final LowMemDetector mLowMemDetector;
 
+    static final String EXTRA_TITLE = "android.intent.extra.TITLE";
+    static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
+    static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+
     /** All system services */
     SystemServiceManager mSystemServiceManager;
 
@@ -1135,7 +1144,7 @@
     /**
      * Access to DeviceIdleController service.
      */
-    DeviceIdleController.LocalService mLocalDeviceIdleController;
+    DeviceIdleInternal mLocalDeviceIdleController;
 
     /**
      * Power-save whitelisted app-ids (not including except-idle-whitelisted ones).
@@ -1480,8 +1489,9 @@
     public ActivityTaskManagerService mActivityTaskManager;
     @VisibleForTesting
     public ActivityTaskManagerInternal mAtmInternal;
+    UriGrantsManagerInternal mUgmInternal;
     @VisibleForTesting
-    public UriGrantsManagerInternal mUgmInternal;
+    public final ActivityManagerInternal mInternal;
     final ActivityThread mSystemThread;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -2413,6 +2423,8 @@
         mProcStartHandler = null;
         mHiddenApiBlacklist = null;
         mFactoryTest = FACTORY_TEST_OFF;
+        mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+        mInternal = new LocalService();
     }
 
     // Note: This method is invoked on the main thread but may need to attach various
@@ -2566,6 +2578,7 @@
             Slog.w(TAG, "Setting background thread cpuset failed");
         }
 
+        mInternal = new LocalService();
     }
 
     public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -2583,7 +2596,7 @@
         mBatteryStatsService.publish();
         mAppOpsService.publish(mContext);
         Slog.d("AppOps", "AppOpsService published");
-        LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+        LocalServices.addService(ActivityManagerInternal.class, mInternal);
         mActivityTaskManager.onActivityManagerInternalAdded();
         mUgmInternal.onActivityManagerInternalAdded();
         mPendingIntentController.onActivityManagerInternalAdded();
@@ -8197,6 +8210,53 @@
     @Deprecated
     @Override
     public void requestBugReport(int bugreportType) {
+        requestBugReportWithDescription(null, null, bugreportType);
+    }
+
+    /**
+     * @deprecated This method is only used by a few internal components and it will soon be
+     * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+     * No new code should be calling it.
+     */
+    @Deprecated
+    public void requestBugReportWithDescription(@Nullable String shareTitle,
+            @Nullable String shareDescription, int bugreportType) {
+        if (!TextUtils.isEmpty(shareTitle)) {
+            if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
+                String errorStr = "shareTitle should be less than " +
+                        MAX_BUGREPORT_TITLE_SIZE + " characters";
+                throw new IllegalArgumentException(errorStr);
+            }
+            if (!TextUtils.isEmpty(shareDescription)) {
+                int length = shareDescription.getBytes(StandardCharsets.UTF_8).length;
+                if (length > SystemProperties.PROP_VALUE_MAX) {
+                    String errorStr = "shareTitle should be less than " +
+                            SystemProperties.PROP_VALUE_MAX + " bytes";
+                    throw new IllegalArgumentException(errorStr);
+                } else {
+                    SystemProperties.set("dumpstate.options.description", shareDescription);
+                }
+            }
+            SystemProperties.set("dumpstate.options.title", shareTitle);
+            Slog.d(TAG, "Bugreport notification title " + shareTitle
+                    + " description " + shareDescription);
+        }
+        final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
+                FeatureFlagUtils.USE_BUGREPORT_API);
+
+        if (useApi) {
+            // Create intent to trigger Bugreport API via Shell
+            Intent triggerShellBugreport = new Intent();
+            triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
+            triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
+            triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
+            if (shareTitle != null) {
+                triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
+            }
+            if (shareDescription != null) {
+                triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
+            }
+        }
         String extraOptions = null;
         switch (bugreportType) {
             case ActivityManager.BUGREPORT_OPTION_FULL:
@@ -8238,45 +8298,6 @@
      * No new code should be calling it.
      */
     @Deprecated
-    private void requestBugReportWithDescription(String shareTitle, String shareDescription,
-                                                 int bugreportType) {
-        if (!TextUtils.isEmpty(shareTitle)) {
-            if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
-                String errorStr = "shareTitle should be less than " +
-                        MAX_BUGREPORT_TITLE_SIZE + " characters";
-                throw new IllegalArgumentException(errorStr);
-            } else {
-                if (!TextUtils.isEmpty(shareDescription)) {
-                    int length;
-                    try {
-                        length = shareDescription.getBytes("UTF-8").length;
-                    } catch (UnsupportedEncodingException e) {
-                        String errorStr = "shareDescription: UnsupportedEncodingException";
-                        throw new IllegalArgumentException(errorStr);
-                    }
-                    if (length > SystemProperties.PROP_VALUE_MAX) {
-                        String errorStr = "shareTitle should be less than " +
-                                SystemProperties.PROP_VALUE_MAX + " bytes";
-                        throw new IllegalArgumentException(errorStr);
-                    } else {
-                        SystemProperties.set("dumpstate.options.description", shareDescription);
-                    }
-                }
-                SystemProperties.set("dumpstate.options.title", shareTitle);
-            }
-        }
-
-        Slog.d(TAG, "Bugreport notification title " + shareTitle
-                + " description " + shareDescription);
-        requestBugReport(bugreportType);
-    }
-
-    /**
-     * @deprecated This method is only used by a few internal components and it will soon be
-     * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
-     * No new code should be calling it.
-     */
-    @Deprecated
     @Override
     public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
         requestBugReportWithDescription(shareTitle, shareDescription,
@@ -8990,6 +9011,7 @@
      */
     public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
         t.traceBegin("PhaseActivityManagerReady");
+        mSystemServiceManager.preSystemReady();
         synchronized(this) {
             if (mSystemReady) {
                 // If we're done calling all the receivers, run the next "boot phase" passed in
@@ -9002,8 +9024,8 @@
             }
 
             t.traceBegin("controllersReady");
-            mLocalDeviceIdleController
-                    = LocalServices.getService(DeviceIdleController.LocalService.class);
+            mLocalDeviceIdleController =
+                    LocalServices.getService(DeviceIdleInternal.class);
             mActivityTaskManager.onSystemReady();
             // Make sure we have the current profile info, since it is needed for security checks.
             mUserController.onSystemReady();
@@ -9139,8 +9161,12 @@
             mAtmInternal.showSystemReadyErrorDialogsIfNeeded();
             t.traceEnd();
 
-            boolean isSystemUser = currentUserId == UserHandle.USER_SYSTEM;
-            if (isSystemUser) {
+            // Some systems - like automotive - will explicitly unlock system user then switch
+            // to a secondary user. Hence, we don't want to send duplicate broadcasts for the
+            // system user here.
+            boolean sendSystemUserBroadcasts = currentUserId == UserHandle.USER_SYSTEM;
+
+            if (sendSystemUserBroadcasts) {
                 t.traceBegin("sendUserStartBroadcast");
                 final int callingUid = Binder.getCallingUid();
                 final int callingPid = Binder.getCallingPid();
@@ -9181,7 +9207,7 @@
             mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
             t.traceEnd();
 
-            if (isSystemUser) {
+            if (sendSystemUserBroadcasts) {
                 t.traceBegin("sendUserSwitchBroadcasts");
                 mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
                 t.traceEnd();
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9239d03..a47ea4f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -756,7 +756,7 @@
     }
 
     public void noteStartAudio(int uid) {
-        enforceSelfOrCallingPermission(uid);
+        enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteAudioOnLocked(uid);
             StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, uid, null,
@@ -765,7 +765,7 @@
     }
 
     public void noteStopAudio(int uid) {
-        enforceSelfOrCallingPermission(uid);
+        enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteAudioOffLocked(uid);
             StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, uid, null,
@@ -774,7 +774,7 @@
     }
 
     public void noteStartVideo(int uid) {
-        enforceSelfOrCallingPermission(uid);
+        enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOnLocked(uid);
             StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid, null,
@@ -783,7 +783,7 @@
     }
 
     public void noteStopVideo(int uid) {
-        enforceSelfOrCallingPermission(uid);
+        enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOffLocked(uid);
             StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid,
@@ -1184,13 +1184,6 @@
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
 
-    private void enforceSelfOrCallingPermission(int uid) {
-        if (Binder.getCallingUid() == uid) {
-            return;
-        }
-        enforceCallingPermission();
-    }
-
     final class WakeupReasonThread extends Thread {
         private static final int MAX_REASON_SIZE = 512;
         private CharsetDecoder mDecoder;
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 9cf342c..ace0a7d 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -291,7 +291,7 @@
                 makeRelativeToEpochStart(currentOps, nowMillis);
                 currentOps.accept(visitor);
 
-                if(isPersistenceInitializedMLocked()) {
+                if (!isPersistenceInitializedMLocked()) {
                     Slog.e(LOG_TAG, "Interaction before persistence initialized");
                     return;
                 }
@@ -457,7 +457,7 @@
                 // it is a part of the persistence initialization process.
                 boolean resampleHistory = false;
                 Slog.i(LOG_TAG, "New history parameters: mode:"
-                        + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:"
+                        + AppOpsManager.historicalModeToString(mode) + " baseSnapshotInterval:"
                         + baseSnapshotInterval + " intervalCompressionMultiplier:"
                         + intervalCompressionMultiplier);
                 if (mMode != mode) {
@@ -1066,7 +1066,7 @@
                 normalizeSnapshotForSlotDuration(persistedOps, slotDurationMillis);
                 writeHistoricalOpsDLocked(persistedOps, intervalOverflowMillis, newFile);
                 if (DEBUG) {
-                    Slog.i(LOG_TAG, "Persisted at depth: " + depth
+                    Slog.i(LOG_TAG, "Persisted at depth: " + depth + " file: " + newFile
                             + " ops:\n" + opsToDebugString(persistedOps));
                     enforceOpsWellFormed(persistedOps);
                 }
@@ -1160,7 +1160,7 @@
             }
             if (DEBUG) {
                 if (allOps != null) {
-                    Slog.i(LOG_TAG, "Read from file: " + file + "ops:\n"
+                    Slog.i(LOG_TAG, "Read from file: " + file + " ops:\n"
                             + opsToDebugString(allOps));
                     enforceOpsWellFormed(allOps);
                 }
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 4a9ccde..766e5c4 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -209,8 +209,7 @@
                     // will show briefly and be replaced by "device locked out" message.
                     if (listener != null) {
                         if (isBiometricPrompt()) {
-                            listener.onAuthenticationFailedInternal(getCookie(),
-                                    getRequireConfirmation());
+                            listener.onAuthenticationFailedInternal();
                         } else {
                             listener.onAuthenticationFailed(getHalDeviceId());
                         }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index af2f24f..24e6a75 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,11 +25,9 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
 
 import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
-import android.app.IActivityTaskManager;
+import android.app.IActivityManager;
 import android.app.KeyguardManager;
-import android.app.TaskStackListener;
 import android.app.UserSwitchObserver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -69,6 +67,7 @@
 import android.util.StatsLog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.server.SystemService;
@@ -88,9 +87,8 @@
     private static final String TAG = "BiometricService";
     private static final boolean DEBUG = true;
 
-    private static final int MSG_ON_TASK_STACK_CHANGED = 1;
     private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
-    private static final int MSG_ON_AUTHENTICATION_FAILED = 3;
+    private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
     private static final int MSG_ON_ERROR = 4;
     private static final int MSG_ON_ACQUIRED = 5;
     private static final int MSG_ON_DISMISSED = 6;
@@ -101,6 +99,7 @@
     private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
     private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
     private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;
+    private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 14;
 
     private static final int[] FEATURE_ID = {
         TYPE_FINGERPRINT,
@@ -112,33 +111,41 @@
      * Authentication either just called and we have not transitioned to the CALLED state, or
      * authentication terminated (success or error).
      */
-    private static final int STATE_AUTH_IDLE = 0;
+    static final int STATE_AUTH_IDLE = 0;
     /**
      * Authentication was called and we are waiting for the <Biometric>Services to return their
      * cookies before starting the hardware and showing the BiometricPrompt.
      */
-    private static final int STATE_AUTH_CALLED = 1;
+    static final int STATE_AUTH_CALLED = 1;
     /**
      * Authentication started, BiometricPrompt is showing and the hardware is authenticating.
      */
-    private static final int STATE_AUTH_STARTED = 2;
+    static final int STATE_AUTH_STARTED = 2;
     /**
      * Authentication is paused, waiting for the user to press "try again" button. Only
      * passive modalities such as Face or Iris should have this state. Note that for passive
      * modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
      * fingerprint.
      */
-    private static final int STATE_AUTH_PAUSED = 3;
+    static final int STATE_AUTH_PAUSED = 3;
     /**
      * Authentication is successful, but we're waiting for the user to press "confirm" button.
      */
-    private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+    static final int STATE_AUTH_PENDING_CONFIRM = 5;
     /**
      * Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
      */
-    private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
+    static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
+    /**
+     * Biometric authenticated, waiting for SysUI to finish animation
+     */
+    static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
+    /**
+     * Biometric error, waiting for SysUI to finish animation
+     */
+    static final int STATE_ERROR_PENDING_SYSUI = 8;
 
-    private final class AuthSession implements IBinder.DeathRecipient {
+    final class AuthSession implements IBinder.DeathRecipient {
         // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
         // <Biometric>Services before we can start authenticating. Pairs that have been returned
         // are moved to mModalitiesMatched.
@@ -165,10 +172,13 @@
         final boolean mRequireConfirmation;
 
         // The current state, which can be either idle, called, or started
-        private int mState = STATE_AUTH_IDLE;
+        int mState = STATE_AUTH_IDLE;
         // For explicit confirmation, do not send to keystore until the user has confirmed
         // the authentication.
         byte[] mTokenEscrow;
+        // Waiting for SystemUI to complete animation
+        int mErrorEscrow;
+        String mErrorStringEscrow;
 
         // Timestamp when authentication started
         private long mStartTimeMs;
@@ -244,42 +254,37 @@
         }
     }
 
-    private final class BiometricTaskStackListener extends TaskStackListener {
-        @Override
-        public void onTaskStackChanged() {
-            mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
-        }
-    }
-
+    private final Injector mInjector;
+    @VisibleForTesting
+    final IBiometricService.Stub mImpl;
     private final AppOpsManager mAppOps;
     private final boolean mHasFeatureFingerprint;
     private final boolean mHasFeatureIris;
     private final boolean mHasFeatureFace;
-    private final SettingObserver mSettingObserver;
+    @VisibleForTesting
+    SettingObserver mSettingObserver;
     private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
-    private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
     private final Random mRandom = new Random();
 
-    private IFingerprintService mFingerprintService;
-    private IFaceService mFaceService;
-    private IActivityTaskManager mActivityTaskManager;
-    private IStatusBarService mStatusBarService;
+    @VisibleForTesting
+    IFingerprintService mFingerprintService;
+    @VisibleForTesting
+    IFaceService mFaceService;
+    @VisibleForTesting
+    IStatusBarService mStatusBarService;
+    @VisibleForTesting
+    KeyStore mKeyStore;
 
     // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
     // polymorphism :/
     final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
 
-    // Cache the current service that's being used. This is the service which
-    // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
-    // check (is caller the current client) is done in the <Biometric>Service.
-    // Since Settings/System (not application) is responsible for changing preference, this
-    // should be safe.
-    private int mCurrentModality;
-
     // The current authentication session, null if idle/done. We need to track both the current
     // and pending sessions since errors may be sent to either.
-    private AuthSession mCurrentAuthSession;
-    private AuthSession mPendingAuthSession;
+    @VisibleForTesting
+    AuthSession mCurrentAuthSession;
+    @VisibleForTesting
+    AuthSession mPendingAuthSession;
 
     // TODO(b/123378871): Remove when moved.
     // When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
@@ -289,15 +294,11 @@
     // to this receiver.
     private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+    @VisibleForTesting
+    final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_ON_TASK_STACK_CHANGED: {
-                    handleTaskStackChanged();
-                    break;
-                }
-
                 case MSG_ON_AUTHENTICATION_SUCCEEDED: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     handleAuthenticationSucceeded(
@@ -307,8 +308,8 @@
                     break;
                 }
 
-                case MSG_ON_AUTHENTICATION_FAILED: {
-                    handleAuthenticationFailed((String) msg.obj /* failureReason */);
+                case MSG_ON_AUTHENTICATION_REJECTED: {
+                    handleAuthenticationRejected((String) msg.obj /* failureReason */);
                     break;
                 }
 
@@ -397,6 +398,11 @@
                     break;
                 }
 
+                case MSG_ON_AUTHENTICATION_TIMED_OUT: {
+                    handleAuthenticationTimedOut((String) msg.obj /* errorMessage */);
+                    break;
+                }
+
                 default:
                     Slog.e(TAG, "Unknown message: " + msg);
                     break;
@@ -422,7 +428,8 @@
         }
     }
 
-    private final class SettingObserver extends ContentObserver {
+    @VisibleForTesting
+    public static class SettingObserver extends ContentObserver {
 
         private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
         private static final boolean DEFAULT_APP_ENABLED = true;
@@ -436,6 +443,7 @@
                 Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
 
         private final ContentResolver mContentResolver;
+        private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
 
         private Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
         private Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
@@ -446,13 +454,15 @@
          *
          * @param handler The handler to run {@link #onChange} on, or null if none.
          */
-        SettingObserver(Handler handler) {
+        public SettingObserver(Context context, Handler handler,
+                List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
             super(handler);
-            mContentResolver = getContext().getContentResolver();
+            mContentResolver = context.getContentResolver();
+            mCallbacks = callbacks;
             updateContentObserver();
         }
 
-        void updateContentObserver() {
+        public void updateContentObserver() {
             mContentResolver.unregisterContentObserver(this);
             mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
                     false /* notifyForDescendents */,
@@ -495,7 +505,7 @@
             }
         }
 
-        boolean getFaceEnabledOnKeyguard() {
+        public boolean getFaceEnabledOnKeyguard() {
             final int user = ActivityManager.getCurrentUser();
             if (!mFaceEnabledOnKeyguard.containsKey(user)) {
                 onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
@@ -503,22 +513,23 @@
             return mFaceEnabledOnKeyguard.get(user);
         }
 
-        boolean getFaceEnabledForApps(int userId) {
+        public boolean getFaceEnabledForApps(int userId) {
+            Slog.e(TAG, "getFaceEnabledForApps: " + userId, new Exception());
             if (!mFaceEnabledForApps.containsKey(userId)) {
                 onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
             }
             return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
         }
 
-        boolean getFaceAlwaysRequireConfirmation(int userId) {
+        public boolean getFaceAlwaysRequireConfirmation(int userId) {
             if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
                 onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
             }
             return mFaceAlwaysRequireConfirmation.get(userId);
         }
 
-        void notifyEnabledOnKeyguardCallbacks(int userId) {
-            List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
+        public void notifyEnabledOnKeyguardCallbacks(int userId) {
+            List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
             for (int i = 0; i < callbacks.size(); i++) {
                 callbacks.get(i).notify(BiometricSourceType.FACE,
                         mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED),
@@ -527,7 +538,7 @@
         }
     }
 
-    private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
+    final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
 
         private final IBiometricEnabledOnKeyguardCallback mCallback;
 
@@ -559,7 +570,8 @@
     }
 
     // Wrap the client's receiver so we can do things with the BiometricDialog first
-    private final IBiometricServiceReceiverInternal mInternalReceiver =
+    @VisibleForTesting
+    final IBiometricServiceReceiverInternal mInternalReceiver =
             new IBiometricServiceReceiverInternal.Stub() {
         @Override
         public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
@@ -571,10 +583,11 @@
         }
 
         @Override
-        public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+        public void onAuthenticationFailed()
                 throws RemoteException {
             String failureReason = getContext().getString(R.string.biometric_not_recognized);
-            mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
+            Slog.v(TAG, "onAuthenticationFailed: " + failureReason);
+            mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED, failureReason).sendToTarget();
         }
 
         @Override
@@ -583,7 +596,7 @@
             // soft errors and we should allow the user to try authenticating again instead of
             // dismissing BiometricPrompt.
             if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
-                mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
+                mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, message).sendToTarget();
             } else {
                 SomeArgs args = SomeArgs.obtain();
                 args.argi1 = cookie;
@@ -873,6 +886,44 @@
         }
     }
 
+    @VisibleForTesting
+    static class Injector {
+        IActivityManager getActivityManagerService() {
+            return ActivityManager.getService();
+        }
+
+        IStatusBarService getStatusBarService() {
+            return IStatusBarService.Stub.asInterface(
+                    ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        }
+
+        IFingerprintService getFingerprintService() {
+            return IFingerprintService.Stub.asInterface(
+                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+        }
+
+        IFaceService getFaceService() {
+            return IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE));
+        }
+
+        SettingObserver getSettingObserver(Context context, Handler handler,
+                List<EnabledOnKeyguardCallback> callbacks) {
+            return new SettingObserver(context, handler, callbacks);
+        }
+
+        KeyStore getKeyStore() {
+            return KeyStore.getInstance();
+        }
+
+        boolean isDebugEnabled(Context context, int userId) {
+            return Utils.isDebugEnabled(context, userId);
+        }
+
+        void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+            service.publishBinderService(Context.BIOMETRIC_SERVICE, impl);
+        }
+    }
+
     /**
      * Initializes the system service.
      * <p>
@@ -883,11 +934,19 @@
      * @param context The system server context.
      */
     public BiometricService(Context context) {
+        this(context, new Injector());
+    }
+
+    @VisibleForTesting
+    BiometricService(Context context, Injector injector) {
         super(context);
 
+        mInjector = injector;
+        mImpl = new BiometricServiceWrapper();
         mAppOps = context.getSystemService(AppOpsManager.class);
         mEnabledOnKeyguardCallbacks = new ArrayList<>();
-        mSettingObserver = new SettingObserver(mHandler);
+        mSettingObserver = mInjector.getSettingObserver(context, mHandler,
+                mEnabledOnKeyguardCallbacks);
 
         final PackageManager pm = context.getPackageManager();
         mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
@@ -895,7 +954,7 @@
         mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
 
         try {
-            ActivityManager.getService().registerUserSwitchObserver(
+            injector.getActivityManagerService().registerUserSwitchObserver(
                     new UserSwitchObserver() {
                         @Override
                         public void onUserSwitchComplete(int newUserId) {
@@ -913,17 +972,14 @@
     public void onStart() {
         // TODO: maybe get these on-demand
         if (mHasFeatureFingerprint) {
-            mFingerprintService = IFingerprintService.Stub.asInterface(
-                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+            mFingerprintService = mInjector.getFingerprintService();
         }
         if (mHasFeatureFace) {
-            mFaceService = IFaceService.Stub.asInterface(
-                    ServiceManager.getService(Context.FACE_SERVICE));
+            mFaceService = mInjector.getFaceService();
         }
 
-        mActivityTaskManager = ActivityTaskManager.getService();
-        mStatusBarService = IStatusBarService.Stub.asInterface(
-                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+        mKeyStore = mInjector.getKeyStore();
+        mStatusBarService = mInjector.getStatusBarService();
 
         // Cache the authenticators
         for (int i = 0; i < FEATURE_ID.length; i++) {
@@ -934,7 +990,7 @@
             }
         }
 
-        publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
+        mInjector.publishBinderService(this, mImpl);
     }
 
     /**
@@ -1068,7 +1124,7 @@
     }
 
     private void logDialogDismissed(int reason) {
-        if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+        if (reason == BiometricPrompt.DISMISSED_REASON_CONFIRMED) {
             // Explicit auth, authentication confirmed.
             // Latency in this case is authenticated -> confirmed. <Biometric>Service
             // should have the first half (first acquired -> authenticated).
@@ -1094,7 +1150,7 @@
                     mCurrentAuthSession.mRequireConfirmation,
                     StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
                     latency,
-                    Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
+                    mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
         } else {
 
             final long latency = System.currentTimeMillis() - mCurrentAuthSession.mStartTimeMs;
@@ -1122,7 +1178,7 @@
                     BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
                     error,
                     0 /* vendorCode */,
-                    Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
+                    mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
                     latency);
         }
     }
@@ -1145,51 +1201,22 @@
         return modality;
     }
 
-    private void handleTaskStackChanged() {
-        try {
-            final List<ActivityManager.RunningTaskInfo> runningTasks =
-                    mActivityTaskManager.getTasks(1);
-            if (!runningTasks.isEmpty()) {
-                final String topPackage = runningTasks.get(0).topActivity.getPackageName();
-                if (mCurrentAuthSession != null
-                        && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
-                    mStatusBarService.hideBiometricDialog();
-                    mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                    mCurrentAuthSession.mClientReceiver.onError(
-                            BiometricConstants.BIOMETRIC_ERROR_CANCELED,
-                            getContext().getString(
-                                    com.android.internal.R.string.biometric_error_canceled)
-                    );
-                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                    mCurrentAuthSession = null;
-                }
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to get running tasks", e);
-        }
-    }
-
     private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
-
         try {
             // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
             // after user dismissed/canceled dialog).
             if (mCurrentAuthSession == null) {
-                Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
+                Slog.e(TAG, "handleAuthenticationSucceeded: Auth session is null");
                 return;
             }
 
+            // Store the auth token and submit it to keystore after the dialog is confirmed /
+            // animating away.
+            mCurrentAuthSession.mTokenEscrow = token;
             if (!requireConfirmation) {
-                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                KeyStore.getInstance().addAuthToken(token);
-                mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                mCurrentAuthSession = null;
+                mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI;
             } else {
                 mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
-                // Store the auth token and submit it to keystore after the confirmation
-                // button has been pressed.
-                mCurrentAuthSession.mTokenEscrow = token;
                 mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
             }
 
@@ -1201,12 +1228,13 @@
         }
     }
 
-    private void handleAuthenticationFailed(String failureReason) {
+    private void handleAuthenticationRejected(String failureReason) {
+        Slog.v(TAG, "handleAuthenticationRejected: " + failureReason);
         try {
             // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
             // after user dismissed/canceled dialog).
             if (mCurrentAuthSession == null) {
-                Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
+                Slog.e(TAG, "handleAuthenticationRejected: Auth session is null");
                 return;
             }
 
@@ -1225,16 +1253,31 @@
         }
     }
 
+    private void handleAuthenticationTimedOut(String message) {
+        Slog.v(TAG, "handleAuthenticationTimedOut: " + message);
+        try {
+            // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+            // after user dismissed/canceled dialog).
+            if (mCurrentAuthSession == null) {
+                Slog.e(TAG, "handleAuthenticationTimedOut: Auth session is null");
+                return;
+            }
+
+            mStatusBarService.onBiometricAuthenticated(false, message);
+            mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Remote exception", e);
+        }
+    }
+
     private void handleOnConfirmDeviceCredentialSuccess() {
         if (mConfirmDeviceCredentialReceiver == null) {
-            Slog.w(TAG, "onCDCASuccess null!");
+            Slog.w(TAG, "handleOnConfirmDeviceCredentialSuccess null!");
             return;
         }
         try {
-            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
             mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
             if (mCurrentAuthSession != null) {
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                 mCurrentAuthSession = null;
             }
         } catch (RemoteException e) {
@@ -1245,14 +1288,13 @@
 
     private void handleOnConfirmDeviceCredentialError(int error, String message) {
         if (mConfirmDeviceCredentialReceiver == null) {
-            Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
+            Slog.w(TAG, "handleOnConfirmDeviceCredentialError null! Error: "
+                    + error + " " + message);
             return;
         }
         try {
-            mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
             mConfirmDeviceCredentialReceiver.onError(error, message);
             if (mCurrentAuthSession != null) {
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                 mCurrentAuthSession = null;
             }
         } catch (RemoteException e) {
@@ -1272,7 +1314,7 @@
     }
 
     private void handleOnError(int cookie, int error, String message) {
-        Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+        Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
         // Errors can either be from the current auth session or the pending auth session.
         // The pending auth session may receive errors such as ERROR_LOCKOUT before
         // it becomes the current auth session. Similarly, the current auth session may
@@ -1282,6 +1324,9 @@
         try {
             if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
 
+                mCurrentAuthSession.mErrorEscrow = error;
+                mCurrentAuthSession.mErrorStringEscrow = message;
+
                 if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
                     // If we were invoked by ConfirmDeviceCredential, do not delete the current
                     // auth session since we still need to respond to cancel signal while
@@ -1293,39 +1338,18 @@
                     mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
                     mStatusBarService.hideBiometricDialog();
                 } else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
-                    mStatusBarService.onBiometricError(message);
+                    mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
                     if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
-                        mActivityTaskManager.unregisterTaskStackListener(
-                                mTaskStackListener);
-                        mCurrentAuthSession.mClientReceiver.onError(error, message);
-                        mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                        mCurrentAuthSession = null;
                         mStatusBarService.hideBiometricDialog();
                     } else {
-                        // Send errors after the dialog is dismissed.
-                        mHandler.postDelayed(() -> {
-                            try {
-                                if (mCurrentAuthSession != null) {
-                                    mActivityTaskManager.unregisterTaskStackListener(
-                                            mTaskStackListener);
-                                    mCurrentAuthSession.mClientReceiver.onError(error,
-                                            message);
-                                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                                    mCurrentAuthSession = null;
-                                }
-                            } catch (RemoteException e) {
-                                Slog.e(TAG, "Remote exception", e);
-                            }
-                        }, BiometricPrompt.HIDE_DIALOG_DELAY);
+                        mStatusBarService.onBiometricError(message);
                     }
                 } else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
                     // In the "try again" state, we should forward canceled errors to
-                    // the client and and clean up.
+                    // the client and and clean up. The only error we should get here is
+                    // ERROR_CANCELED due to another client kicking us out.
                     mCurrentAuthSession.mClientReceiver.onError(error, message);
-                    mStatusBarService.onBiometricError(message);
-                    mActivityTaskManager.unregisterTaskStackListener(
-                            mTaskStackListener);
-                    mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+                    mStatusBarService.hideBiometricDialog();
                     mCurrentAuthSession = null;
                 } else {
                     Slog.e(TAG, "Impossible session error state: "
@@ -1335,7 +1359,6 @@
                     && mPendingAuthSession.containsCookie(cookie)) {
                 if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
                     mPendingAuthSession.mClientReceiver.onError(error, message);
-                    mPendingAuthSession.mState = STATE_AUTH_IDLE;
                     mPendingAuthSession = null;
                 } else {
                     Slog.e(TAG, "Impossible pending session error state: "
@@ -1370,42 +1393,50 @@
 
     private void handleOnDismissed(int reason) {
         if (mCurrentAuthSession == null) {
-            Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
+            Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
             return;
         }
 
         logDialogDismissed(reason);
 
         try {
-            if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
-                // Positive button is used by passive modalities as a "confirm" button,
-                // do not send to client
-                mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
-                // Cancel authentication. Skip the token/package check since we are cancelling
-                // from system server. The interface is permission protected so this is fine.
-                cancelInternal(null /* token */, null /* package */, false /* fromClient */);
-            }
-            if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
-                mCurrentAuthSession.mClientReceiver.onError(
-                        BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
-                        getContext().getString(
-                                com.android.internal.R.string.biometric_error_user_canceled));
-            } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
-                // Have the service send the token to KeyStore, and send onAuthenticated
-                // to the application
-                KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
-                mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+            switch (reason) {
+                case BiometricPrompt.DISMISSED_REASON_CONFIRMED:
+                case BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED:
+                    mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+                    mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+                    break;
+
+                case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
+                    mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+                    // Cancel authentication. Skip the token/package check since we are cancelling
+                    // from system server. The interface is permission protected so this is fine.
+                    cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+                    break;
+
+                case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
+                    mCurrentAuthSession.mClientReceiver.onError(
+                            BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+                            getContext().getString(R.string.biometric_error_user_canceled));
+                    // Cancel authentication. Skip the token/package check since we are cancelling
+                    // from system server. The interface is permission protected so this is fine.
+                    cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+                    break;
+
+                case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
+                case BiometricPrompt.DISMISSED_REASON_ERROR:
+                    mCurrentAuthSession.mClientReceiver.onError(mCurrentAuthSession.mErrorEscrow,
+                            mCurrentAuthSession.mErrorStringEscrow);
+                    break;
+
+                default:
+                    Slog.w(TAG, "Unhandled reason: " + reason);
+                    break;
             }
 
-            // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the
-            // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when
-            // ConfirmDeviceCredential is confirmed or canceled.
-            // TODO(b/123378871): Remove when moved
-            if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) {
-                mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
-                mCurrentAuthSession = null;
-            }
+            // Dialog is gone, auth session is done.
+            mCurrentAuthSession = null;
+
         } catch (RemoteException e) {
             Slog.e(TAG, "Remote exception", e);
         }
@@ -1472,8 +1503,8 @@
 
                 if (!continuing) {
                     mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
-                            mInternalReceiver, modality, requireConfirmation, userId);
-                    mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+                            mInternalReceiver, modality, requireConfirmation, userId,
+                            mCurrentAuthSession.mOpPackageName);
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote exception", e);
@@ -1517,8 +1548,6 @@
                 return;
             }
 
-            mCurrentModality = modality;
-
             // Start preparing for authentication. Authentication starts when
             // all modalities requested have invoked onReadyForAuthentication.
             authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
@@ -1610,7 +1639,6 @@
                                 com.android.internal.R.string.biometric_error_user_canceled)
                 );
 
-                mCurrentAuthSession.mState = STATE_AUTH_IDLE;
                 mCurrentAuthSession = null;
                 mStatusBarService.hideBiometricDialog();
             } catch (RemoteException e) {
@@ -1637,25 +1665,31 @@
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         final int callingUserId = UserHandle.getCallingUserId();
-        mHandler.post(() -> {
-            try {
-                // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
-                // drivers have canceled authentication.
-                if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
-                    mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
-                            callingUid, callingPid, callingUserId, fromClient);
-                }
-                if ((mCurrentModality & TYPE_IRIS) != 0) {
-                    Slog.w(TAG, "Iris unsupported");
-                }
-                if ((mCurrentModality & TYPE_FACE) != 0) {
-                    mFaceService.cancelAuthenticationFromService(token, opPackageName,
-                            callingUid, callingPid, callingUserId, fromClient);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to cancel authentication");
-            }
-        });
-    }
 
+        try {
+            if (mCurrentAuthSession == null) {
+                Slog.w(TAG, "Skipping cancelInternal");
+                return;
+            } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+                Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
+                return;
+            }
+
+            // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+            // drivers have canceled authentication.
+            if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) {
+                mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+                        callingUid, callingPid, callingUserId, fromClient);
+            }
+            if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) {
+                Slog.w(TAG, "Iris unsupported");
+            }
+            if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
+                mFaceService.cancelAuthenticationFromService(token, opPackageName,
+                        callingUid, callingPid, callingUserId, fromClient);
+            }
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to cancel authentication");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index f3f9754..2de18c3 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -420,7 +420,7 @@
             throw new UnsupportedOperationException("Stub!");
         }
 
-        default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+        default void onAuthenticationFailedInternal()
                 throws RemoteException {
             throw new UnsupportedOperationException("Stub!");
         }
@@ -457,10 +457,10 @@
         }
 
         @Override
-        public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+        public void onAuthenticationFailedInternal()
                 throws RemoteException {
             if (getWrapperReceiver() != null) {
-                getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+                getWrapperReceiver().onAuthenticationFailed();
             }
         }
     }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 53890a4..a0eafb4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -35,6 +35,8 @@
 import android.hardware.broadcastradio.V2_0.VendorKeyValue;
 import android.hardware.radio.RadioManager;
 import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.MutableInt;
 import android.util.Slog;
@@ -45,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -56,6 +59,7 @@
     @NonNull public final RadioManager.ModuleProperties mProperties;
 
     private final Object mLock = new Object();
+    @NonNull private final Handler mHandler;
 
     @GuardedBy("mLock")
     private ITunerSession mHalTunerSession;
@@ -77,22 +81,24 @@
     private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
         @Override
         public void onTuneFailed(int result, ProgramSelector programSelector) {
-            fanoutAidlCallback(cb -> cb.onTuneFailed(result, Convert.programSelectorFromHal(
-                    programSelector)));
+            lockAndFireLater(() -> {
+                android.hardware.radio.ProgramSelector csel =
+                        Convert.programSelectorFromHal(programSelector);
+                fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+            });
         }
 
         @Override
         public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
-            RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo);
-            synchronized (mLock) {
-                mCurrentProgramInfo = programInfo;
-                fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo));
-            }
+            lockAndFireLater(() -> {
+                mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
+                fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(mCurrentProgramInfo));
+            });
         }
 
         @Override
         public void onProgramListUpdated(ProgramListChunk programListChunk) {
-            synchronized (mLock) {
+            lockAndFireLater(() -> {
                 android.hardware.radio.ProgramList.Chunk chunk =
                         Convert.programListChunkFromHal(programListChunk);
                 mProgramInfoCache.filterAndApplyChunk(chunk);
@@ -100,20 +106,23 @@
                 for (TunerSession tunerSession : mAidlTunerSessions) {
                     tunerSession.onMergedProgramListUpdateFromHal(chunk);
                 }
-            }
+            });
         }
 
         @Override
         public void onAntennaStateChange(boolean connected) {
-            synchronized (mLock) {
+            lockAndFireLater(() -> {
                 mAntennaConnected = connected;
                 fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
-            }
+            });
         }
 
         @Override
         public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
-            fanoutAidlCallback(cb -> cb.onParametersUpdated(Convert.vendorInfoFromHal(parameters)));
+            lockAndFireLater(() -> {
+                Map<String, String> cparam = Convert.vendorInfoFromHal(parameters);
+                fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+            });
         }
     };
 
@@ -126,6 +135,7 @@
             @NonNull RadioManager.ModuleProperties properties) {
         mProperties = Objects.requireNonNull(properties);
         mService = Objects.requireNonNull(service);
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
@@ -310,15 +320,22 @@
         }
     }
 
+    // add to mHandler queue, but ensure the runnable holds mLock when it gets executed
+    private void lockAndFireLater(Runnable r) {
+        mHandler.post(() -> {
+            synchronized (mLock) {
+                r.run();
+            }
+        });
+    }
+
     interface AidlCallbackRunnable {
         void run(android.hardware.radio.ITunerCallback callback) throws RemoteException;
     }
 
     // Invokes runnable with each TunerSession currently open.
     void fanoutAidlCallback(AidlCallbackRunnable runnable) {
-        synchronized (mLock) {
-            fanoutAidlCallbackLocked(runnable);
-        }
+        lockAndFireLater(() -> fanoutAidlCallbackLocked(runnable));
     }
 
     private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 3eea194..27050fa 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,6 +16,7 @@
 
 package com.android.server.compat;
 
+import android.compat.IPlatformCompat;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.util.Slog;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 73d160d..86d1212 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1314,11 +1314,15 @@
             mOffload.excludeDownstreamInterface(who.interfaceName());
             mForwardedDownstreams.remove(who);
 
-            // If this is a Wi-Fi interface, tell WifiManager of any errors.
+            // If this is a Wi-Fi interface, tell WifiManager of any errors
+            // or the inactive serving state.
             if (who.interfaceType() == TETHERING_WIFI) {
                 if (who.lastError() != TETHER_ERROR_NO_ERROR) {
                     getWifiManager().updateInterfaceIpState(
                             who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
+                } else {
+                    getWifiManager().updateInterfaceIpState(
+                            who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index e7a8b13..fb94907 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -95,7 +95,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.ConnectivityService;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -616,8 +616,8 @@
         // a short time, so we can bootstrap the VPN service.
         final long oldId = Binder.clearCallingIdentity();
         try {
-            DeviceIdleController.LocalService idleController =
-                    LocalServices.getService(DeviceIdleController.LocalService.class);
+            DeviceIdleInternal idleController =
+                    LocalServices.getService(DeviceIdleInternal.class);
             idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
                     VPN_LAUNCH_IDLE_WHITELIST_DURATION_MS, mUserHandle, false, "vpn");
 
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
index 836f1e6..f952bce 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -161,6 +161,12 @@
      * Check if cellular upstream is permitted.
      */
     public boolean isCellularUpstreamPermitted() {
+        // If provisioning is required and EntitlementManager don't know any downstream,
+        // cellular upstream should not be allowed.
+        final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+        if (mCurrentTethers.size() == 0 && isTetherProvisioningRequired(config)) {
+            return false;
+        }
         return mCellularUpstreamPermitted;
     }
 
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index f6c49ed..e7f537b 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -102,7 +102,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.function.QuadConsumer;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.accounts.AccountManagerService;
@@ -1634,8 +1634,8 @@
 
         if (syncOperation.syncExemptionFlag
                 == ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET_WITH_TEMP) {
-            DeviceIdleController.LocalService dic =
-                    LocalServices.getService(DeviceIdleController.LocalService.class);
+            DeviceIdleInternal dic =
+                    LocalServices.getService(DeviceIdleInternal.class);
             if (dic != null) {
                 dic.addPowerSaveTempWhitelistApp(Process.SYSTEM_UID,
                         syncOperation.owningPackage,
diff --git a/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
index cc319bf..731d899 100644
--- a/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
+++ b/services/core/java/com/android/server/deviceidle/BluetoothConstraint.java
@@ -29,7 +29,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 
 /**
  * Track whether there are any active Bluetooth devices connected.
@@ -40,14 +40,14 @@
 
     private final Context mContext;
     private final Handler mHandler;
-    private final DeviceIdleController.LocalService mLocalService;
+    private final DeviceIdleInternal mLocalService;
     private final BluetoothManager mBluetoothManager;
 
     private volatile boolean mConnected = true;
     private volatile boolean mMonitoring = false;
 
     public BluetoothConstraint(
-            Context context, Handler handler, DeviceIdleController.LocalService localService) {
+            Context context, Handler handler, DeviceIdleInternal localService) {
         mContext = context;
         mHandler = handler;
         mLocalService = localService;
diff --git a/services/core/java/com/android/server/deviceidle/TvConstraintController.java b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
index 2d472de6..7f0a271 100644
--- a/services/core/java/com/android/server/deviceidle/TvConstraintController.java
+++ b/services/core/java/com/android/server/deviceidle/TvConstraintController.java
@@ -21,7 +21,7 @@
 import android.content.pm.PackageManager;
 import android.os.Handler;
 
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 
 /**
@@ -33,7 +33,7 @@
 public class TvConstraintController implements ConstraintController {
     private final Context mContext;
     private final Handler mHandler;
-    private final DeviceIdleController.LocalService mDeviceIdleService;
+    private final DeviceIdleInternal mDeviceIdleService;
 
     @Nullable
     private final BluetoothConstraint mBluetoothConstraint;
@@ -41,7 +41,7 @@
     public TvConstraintController(Context context, Handler handler) {
         mContext = context;
         mHandler = handler;
-        mDeviceIdleService = LocalServices.getService(DeviceIdleController.LocalService.class);
+        mDeviceIdleService = LocalServices.getService(DeviceIdleInternal.class);
 
         final PackageManager pm = context.getPackageManager();
         mBluetoothConstraint = pm.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index fc59b5b..d9d46b8 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -94,8 +94,8 @@
     private final Handler mHandler;
     private final Listener mListener;
 
-    private final WifiP2pManager mWifiP2pManager;
-    private final Channel mWifiP2pChannel;
+    private WifiP2pManager mWifiP2pManager;
+    private Channel mWifiP2pChannel;
 
     private boolean mWifiP2pEnabled;
     private boolean mWfdEnabled;
@@ -164,9 +164,6 @@
         mHandler = handler;
         mListener = listener;
 
-        mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
-        mWifiP2pChannel = mWifiP2pManager.initialize(context, handler.getLooper(), null);
-
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
         intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
@@ -191,6 +188,18 @@
         updateSettings();
     }
 
+    /**
+     * Used to lazily retrieve WifiP2pManager service.
+     */
+    private void retrieveWifiP2pManagerAndChannel() {
+        if (mWifiP2pManager == null) {
+            mWifiP2pManager = (WifiP2pManager)mContext.getSystemService(Context.WIFI_P2P_SERVICE);
+        }
+        if (mWifiP2pChannel == null && mWifiP2pManager != null) {
+            mWifiP2pChannel = mWifiP2pManager.initialize(mContext, mHandler.getLooper(), null);
+        }
+    }
+
     private void updateSettings() {
         final ContentResolver resolver = mContext.getContentResolver();
         mWifiDisplayOnSetting = Settings.Global.getInt(resolver,
@@ -803,6 +812,9 @@
 
     private void handleStateChanged(boolean enabled) {
         mWifiP2pEnabled = enabled;
+        if (enabled) {
+            retrieveWifiP2pManagerAndChannel();
+        }
         updateWfdEnableState();
     }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f1f6d50..f38f2f9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -106,6 +106,7 @@
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.ILockSettings;
@@ -137,8 +138,10 @@
 import java.security.SecureRandom;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
@@ -169,6 +172,8 @@
     private static final int PROFILE_KEY_IV_SIZE = 12;
     private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
     private static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
+    private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle";
+    private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
 
     // No challenge provided
     private static final int CHALLENGE_NONE = 0;
@@ -357,7 +362,7 @@
             setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId);
             tieProfileLockToParent(managedUserId, newPassword);
             Arrays.fill(newPassword, (byte) 0);
-        } catch (NoSuchAlgorithmException | RemoteException e) {
+        } catch (NoSuchAlgorithmException e) {
             Slog.e(TAG, "Fail to tie managed profile", e);
             // Nothing client can do to fix this issue, so we do not throw exception out
         }
@@ -604,15 +609,11 @@
         if (ks.state(userId) == KeyStore.State.LOCKED
                 && tiedManagedProfileReadyToUnlock(mUserManager.getUserInfo(userId))) {
             Slog.i(TAG, "Managed profile got unlocked, will unlock its keystore");
-            try {
-                // If boot took too long and the password in vold got expired, parent keystore will
-                // be still locked, we ignore this case since the user will be prompted to unlock
-                // the device after boot.
-                unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */,
-                        CHALLENGE_NONE, 0 /* challenge */, null /* resetLockouts */);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to unlock child profile");
-            }
+            // If boot took too long and the password in vold got expired, parent keystore will
+            // be still locked, we ignore this case since the user will be prompted to unlock
+            // the device after boot.
+            unlockChildProfile(userId, true /* ignoreUserNotAuthenticated */,
+                    CHALLENGE_NONE, 0 /* challenge */, null /* resetLockouts */);
         }
     }
 
@@ -648,20 +649,16 @@
                 return;
             }
 
-            try {
-                final long handle = getSyntheticPasswordHandleLocked(userId);
-                final byte[] noCredential = null;
-                AuthenticationResult result =
-                        mSpManager.unwrapPasswordBasedSyntheticPassword(
-                                getGateKeeperService(), handle, noCredential, userId, null);
-                if (result.authToken != null) {
-                    Slog.i(TAG, "Retrieved auth token for user " + userId);
-                    onAuthTokenKnownForUser(userId, result.authToken);
-                } else {
-                    Slog.e(TAG, "Auth token not available for user " + userId);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failure retrieving auth token", e);
+            final long handle = getSyntheticPasswordHandleLocked(userId);
+            final byte[] noCredential = null;
+            AuthenticationResult result =
+                    mSpManager.unwrapPasswordBasedSyntheticPassword(
+                            getGateKeeperService(), handle, noCredential, userId, null);
+            if (result.authToken != null) {
+                Slog.i(TAG, "Retrieved auth token for user " + userId);
+                onAuthTokenKnownForUser(userId, result.authToken);
+            } else {
+                Slog.e(TAG, "Auth token not available for user " + userId);
             }
         }
     }
@@ -698,12 +695,8 @@
         }
         checkWritePermission(UserHandle.USER_SYSTEM);
         migrateOldData();
-        try {
-            getGateKeeperService();
-            mSpManager.initWeaverService();
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
-        }
+        getGateKeeperService();
+        mSpManager.initWeaverService();
         // Find the AuthSecret HAL
         try {
             mAuthSecretService = IAuthSecret.getService();
@@ -872,16 +865,12 @@
     }
 
     private void migrateOldDataAfterSystemReady() {
-        try {
-            // Migrate the FRP credential to the persistent data block
-            if (LockPatternUtils.frpCredentialEnabled(mContext)
-                    && !getBoolean("migrated_frp", false, 0)) {
-                migrateFrpCredential();
-                setBoolean("migrated_frp", true, 0);
-                Slog.i(TAG, "Migrated migrated_frp.");
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Unable to migrateOldDataAfterSystemReady", e);
+        // Migrate the FRP credential to the persistent data block
+        if (LockPatternUtils.frpCredentialEnabled(mContext)
+                && !getBoolean("migrated_frp", false, 0)) {
+            migrateFrpCredential();
+            setBoolean("migrated_frp", true, 0);
+            Slog.i(TAG, "Migrated migrated_frp.");
         }
     }
 
@@ -891,7 +880,7 @@
      * - the FRP credential is not set up
      * - the credential is based on a synthetic password.
      */
-    private void migrateFrpCredential() throws RemoteException {
+    private void migrateFrpCredential() {
         if (mStorage.readPersistentDataBlock() != PersistentData.NONE) {
             return;
         }
@@ -1187,8 +1176,7 @@
 
     private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated,
             @ChallengeType int challengeType, long challenge,
-            @Nullable ArrayList<PendingResetLockout> resetLockouts)
-            throws RemoteException {
+            @Nullable ArrayList<PendingResetLockout> resetLockouts) {
         try {
             doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle),
                     CREDENTIAL_TYPE_PASSWORD,
@@ -1263,14 +1251,10 @@
         for (UserInfo profile : mUserManager.getProfiles(userId)) {
             // Unlock managed profile with unified lock
             if (tiedManagedProfileReadyToUnlock(profile)) {
-                try {
-                    // Must pass the challenge on for resetLockout, so it's not over-written, which
-                    // causes LockSettingsService to revokeChallenge inappropriately.
-                    unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */,
-                            challengeType, challenge, resetLockouts);
-                } catch (RemoteException e) {
-                    Log.d(TAG, "Failed to unlock child profile", e);
-                }
+                // Must pass the challenge on for resetLockout, so it's not over-written, which
+                // causes LockSettingsService to revokeChallenge inappropriately.
+                unlockChildProfile(profile.id, false /* ignoreUserNotAuthenticated */,
+                        challengeType, challenge, resetLockouts);
             }
             // Now we have unlocked the parent user and attempted to unlock the profile we should
             // show notifications if the profile is still locked.
@@ -1350,7 +1334,7 @@
      * terminates when the user is a managed profile.
      */
     private void synchronizeUnifiedWorkChallengeForProfiles(int userId,
-            Map<Integer, byte[]> profilePasswordMap) throws RemoteException {
+            Map<Integer, byte[]> profilePasswordMap) {
         if (mUserManager.getUserInfo(userId).isManagedProfile()) {
             return;
         }
@@ -1464,7 +1448,7 @@
     @Override
     public void setLockCredential(byte[] credential, int type,
             byte[] savedCredential, int requestedQuality, int userId,
-            boolean allowUntrustedChange) throws RemoteException {
+            boolean allowUntrustedChange) {
 
         if (!mLockPatternUtils.hasSecureLockScreen()) {
             throw new UnsupportedOperationException(
@@ -1490,7 +1474,7 @@
      */
     private void setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
             byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
-            boolean isLockTiedToParent) throws RemoteException {
+            boolean isLockTiedToParent) {
         // Normalize savedCredential and credential such that empty string is always represented
         // as null.
         if (savedCredential == null || savedCredential.length == 0) {
@@ -1512,7 +1496,7 @@
                 Slog.wtf(TAG, "CredentialType is none, but credential is non-null.");
             }
             clearUserKeyProtection(userId);
-            getGateKeeperService().clearSecureUserId(userId);
+            gateKeeperClearSecureUserId(userId);
             mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId);
             setKeystorePassword(null, userId);
             fixateNewestUserKeyAuth(userId);
@@ -1523,7 +1507,7 @@
             return;
         }
         if (credential == null) {
-            throw new RemoteException("Null credential with mismatched credential type");
+            throw new IllegalArgumentException("Null credential with mismatched credential type");
         }
 
         CredentialHash currentHandle = mStorage.readCredentialHash(userId);
@@ -1565,8 +1549,13 @@
             CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
             mStorage.writeCredentialHash(willStore, userId);
             // push new secret and auth token to vold
-            GateKeeperResponse gkResponse = getGateKeeperService()
-                    .verifyChallenge(userId, 0, willStore.hash, credential);
+            GateKeeperResponse gkResponse;
+            try {
+                gkResponse = getGateKeeperService().verifyChallenge(userId, 0, willStore.hash,
+                        credential);
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Failed to verify current credential", e);
+            }
             setUserKeyProtection(userId, credential, convertResponse(gkResponse));
             fixateNewestUserKeyAuth(userId);
             // Refresh the auth token
@@ -1576,8 +1565,8 @@
             sendCredentialsOnChangeIfRequired(
                     credentialType, credential, userId, isLockTiedToParent);
         } else {
-            throw new RemoteException("Failed to enroll " +
-                    (credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
+            throw new IllegalStateException(String.format("Failed to enroll %s",
+                    credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
         }
     }
 
@@ -1630,27 +1619,32 @@
         } catch (CertificateException | UnrecoverableKeyException
                 | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
                 | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
-            throw new RuntimeException("Failed to encrypt key", e);
+            throw new IllegalStateException("Failed to encrypt key", e);
         }
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         try {
             if (iv.length != PROFILE_KEY_IV_SIZE) {
-                throw new RuntimeException("Invalid iv length: " + iv.length);
+                throw new IllegalArgumentException("Invalid iv length: " + iv.length);
             }
             outputStream.write(iv);
             outputStream.write(encryptionResult);
         } catch (IOException e) {
-            throw new RuntimeException("Failed to concatenate byte arrays", e);
+            throw new IllegalStateException("Failed to concatenate byte arrays", e);
         }
         mStorage.writeChildProfileLock(userId, outputStream.toByteArray());
     }
 
     private byte[] enrollCredential(byte[] enrolledHandle,
-            byte[] enrolledCredential, byte[] toEnroll, int userId)
-            throws RemoteException {
+            byte[] enrolledCredential, byte[] toEnroll, int userId) {
         checkWritePermission(userId);
-        GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle,
-                enrolledCredential, toEnroll);
+        GateKeeperResponse response;
+        try {
+            response = getGateKeeperService().enroll(userId, enrolledHandle,
+                    enrolledCredential, toEnroll);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to enroll credential", e);
+            return null;
+        }
 
         if (response == null) {
             return null;
@@ -1666,34 +1660,33 @@
         return hash;
     }
 
-    private void setAuthlessUserKeyProtection(int userId, byte[] key) throws RemoteException {
+    private void setAuthlessUserKeyProtection(int userId, byte[] key) {
         if (DEBUG) Slog.d(TAG, "setAuthlessUserKeyProtectiond: user=" + userId);
         addUserKeyAuth(userId, null, key);
     }
 
-    private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr)
-            throws RemoteException {
+    private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr) {
         if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId);
         if (vcr == null) {
-            throw new RemoteException("Null response verifying a credential we just set");
+            throw new IllegalArgumentException("Null response verifying a credential we just set");
         }
         if (vcr.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
-            throw new RemoteException("Non-OK response verifying a credential we just set: "
+            throw new IllegalArgumentException("Non-OK response verifying a credential we just set "
                     + vcr.getResponseCode());
         }
         byte[] token = vcr.getPayload();
         if (token == null) {
-            throw new RemoteException("Empty payload verifying a credential we just set");
+            throw new IllegalArgumentException("Empty payload verifying a credential we just set");
         }
         addUserKeyAuth(userId, token, secretFromCredential(credential));
     }
 
-    private void clearUserKeyProtection(int userId) throws RemoteException {
+    private void clearUserKeyProtection(int userId) {
         if (DEBUG) Slog.d(TAG, "clearUserKeyProtection user=" + userId);
         addUserKeyAuth(userId, null, null);
     }
 
-    private static byte[] secretFromCredential(byte[] credential) throws RemoteException {
+    private static byte[] secretFromCredential(byte[] credential) {
         try {
             MessageDigest digest = MessageDigest.getInstance("SHA-512");
             // Personalize the hash
@@ -1704,7 +1697,7 @@
             digest.update(credential);
             return digest.digest();
         } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
+            throw new IllegalStateException("NoSuchAlgorithmException for SHA-512");
         }
     }
 
@@ -1718,35 +1711,44 @@
     }
 
     /** Unlock disk encryption */
-    private void unlockUserKey(int userId, byte[] token, byte[] secret) throws RemoteException {
+    private void unlockUserKey(int userId, byte[] token, byte[] secret) {
         final UserInfo userInfo = mUserManager.getUserInfo(userId);
-        mStorageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
+        try {
+            mStorageManager.unlockUserKey(userId, userInfo.serialNumber, token, secret);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Failed to unlock user key " + userId, e);
+
+        }
     }
 
-    private void addUserKeyAuth(int userId, byte[] token, byte[] secret)
-            throws RemoteException {
+    private void addUserKeyAuth(int userId, byte[] token, byte[] secret) {
         final UserInfo userInfo = mUserManager.getUserInfo(userId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             mStorageManager.addUserKeyAuth(userId, userInfo.serialNumber, token, secret);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Failed to add new key to vold " + userId, e);
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
     }
 
-    private void fixateNewestUserKeyAuth(int userId)
-            throws RemoteException {
+    private void fixateNewestUserKeyAuth(int userId) {
         if (DEBUG) Slog.d(TAG, "fixateNewestUserKeyAuth: user=" + userId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             mStorageManager.fixateNewestUserKeyAuth(userId);
+        } catch (RemoteException e) {
+            // OK to ignore the exception as vold would just accept both old and new
+            // keys if this call fails, and will fix itself during the next boot
+            Slog.w(TAG, "fixateNewestUserKeyAuth failed", e);
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
     }
 
     @Override
-    public void resetKeyStore(int userId) throws RemoteException {
+    public void resetKeyStore(int userId) {
         checkWritePermission(userId);
         if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId);
         int managedUserId = -1;
@@ -1794,14 +1796,14 @@
 
     @Override
     public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+            ICheckCredentialProgressCallback progressCallback) {
         checkPasswordReadPermission(userId);
         return doVerifyCredential(credential, type, CHALLENGE_NONE, 0, userId, progressCallback);
     }
 
     @Override
     public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge,
-            int userId) throws RemoteException {
+            int userId) {
         checkPasswordReadPermission(userId);
         return doVerifyCredential(credential, type, CHALLENGE_FROM_CALLER, challenge, userId,
                 null /* progressCallback */);
@@ -1809,7 +1811,7 @@
 
     private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
             @ChallengeType int challengeType, long challenge, int userId,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+            ICheckCredentialProgressCallback progressCallback) {
         return doVerifyCredential(credential, credentialType, challengeType, challenge, userId,
                 progressCallback, null /* resetLockouts */);
     }
@@ -1821,7 +1823,7 @@
     private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType,
             @ChallengeType int challengeType, long challenge, int userId,
             ICheckCredentialProgressCallback progressCallback,
-            @Nullable ArrayList<PendingResetLockout> resetLockouts) throws RemoteException {
+            @Nullable ArrayList<PendingResetLockout> resetLockouts) {
         if (credential == null || credential.length == 0) {
             throw new IllegalArgumentException("Credential can't be null or empty");
         }
@@ -1865,10 +1867,10 @@
 
     @Override
     public VerifyCredentialResponse verifyTiedProfileChallenge(byte[] credential, int type,
-            long challenge, int userId) throws RemoteException {
+            long challenge, int userId) {
         checkPasswordReadPermission(userId);
         if (!isManagedProfileWithUnifiedLock(userId)) {
-            throw new RemoteException("User id must be managed profile with unified lock");
+            throw new IllegalArgumentException("User id must be managed profile with unified lock");
         }
         final int parentProfileId = mUserManager.getProfileParent(userId).id;
         // Unlock parent by using parent's challenge
@@ -1896,7 +1898,7 @@
                 | InvalidAlgorithmParameterException | IllegalBlockSizeException
                 | BadPaddingException | CertificateException | IOException e) {
             Slog.e(TAG, "Failed to decrypt child profile key", e);
-            throw new RemoteException("Unable to get tied profile token");
+            throw new IllegalStateException("Unable to get tied profile token");
         }
     }
 
@@ -1907,7 +1909,7 @@
      */
     private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash,
             byte[] credential, @ChallengeType int challengeType, long challenge,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+            ICheckCredentialProgressCallback progressCallback) {
         if ((storedHash == null || storedHash.hash.length == 0)
                     && (credential == null || credential.length == 0)) {
             // don't need to pass empty credentials to GateKeeper
@@ -1922,8 +1924,14 @@
         // of unlocking the user, so yell if calling from the main thread.
         StrictMode.noteDiskRead();
 
-        GateKeeperResponse gateKeeperResponse = getGateKeeperService()
-                .verifyChallenge(userId, challenge, storedHash.hash, credential);
+        GateKeeperResponse gateKeeperResponse;
+        try {
+            gateKeeperResponse = getGateKeeperService()
+                    .verifyChallenge(userId, challenge, storedHash.hash, credential);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "gatekeeper verify failed", e);
+            gateKeeperResponse = GateKeeperResponse.ERROR;
+        }
         VerifyCredentialResponse response = convertResponse(gateKeeperResponse);
         boolean shouldReEnroll = gateKeeperResponse.getShouldReEnroll();
 
@@ -1932,7 +1940,11 @@
             // credential has matched
 
             if (progressCallback != null) {
-                progressCallback.onCredentialVerified();
+                try {
+                    progressCallback.onCredentialVerified();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "progressCallback throws exception", e);
+                }
             }
             notifyActivePasswordMetricsAvailable(storedHash.type, credential, userId);
             unlockKeystore(credential, userId);
@@ -2007,7 +2019,7 @@
     }
 
     @Override
-    public boolean checkVoldPassword(int userId) throws RemoteException {
+    public boolean checkVoldPassword(int userId) {
         if (!mFirstCallToVold) {
             return false;
         }
@@ -2030,6 +2042,9 @@
         try {
             password = service.getPassword();
             service.clearPassword();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "vold getPassword() failed", e);
+            return false;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -2071,14 +2086,7 @@
         final KeyStore ks = KeyStore.getInstance();
         ks.onUserRemoved(userId);
 
-        try {
-            final IGateKeeperService gk = getGateKeeperService();
-            if (gk != null) {
-                gk.clearSecureUserId(userId);
-            }
-        } catch (RemoteException ex) {
-            Slog.w(TAG, "unable to clear GK secure user id");
-        }
+        gateKeeperClearSecureUserId(userId);
         if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) {
             removeKeystoreProfileKey(userId);
         }
@@ -2141,8 +2149,7 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ShellCallback callback, ResultReceiver resultReceiver)
-            throws RemoteException {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         enforceShell();
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -2304,15 +2311,18 @@
         }
     }
 
-    protected synchronized IGateKeeperService getGateKeeperService()
-            throws RemoteException {
+    protected synchronized IGateKeeperService getGateKeeperService() {
         if (mGateKeeperService != null) {
             return mGateKeeperService;
         }
 
         final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE);
         if (service != null) {
-            service.linkToDeath(new GateKeeperDiedRecipient(), 0);
+            try {
+                service.linkToDeath(new GateKeeperDiedRecipient(), 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, " Unable to register death recipient", e);
+            }
             mGateKeeperService = IGateKeeperService.Stub.asInterface(service);
             return mGateKeeperService;
         }
@@ -2321,6 +2331,14 @@
         return null;
     }
 
+    private void gateKeeperClearSecureUserId(int userId) {
+        try {
+            getGateKeeperService().clearSecureUserId(userId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to clear SID", e);
+        }
+    }
+
     /**
      * A user's synthetic password does not change so it must be cached in certain circumstances to
      * enable untrusted credential reset.
@@ -2330,7 +2348,7 @@
      * credential.
      */
     @GuardedBy("mSpManager")
-    private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
+    private SparseArray<AuthenticationToken> mSpCache = new SparseArray<>();
 
     private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
         // Preemptively cache the SP and then try to remove it in a handler.
@@ -2435,8 +2453,7 @@
     @GuardedBy("mSpManager")
     @VisibleForTesting
     protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash,
-            byte[] credential, int credentialType, int requestedQuality,
-            int userId) throws RemoteException {
+            byte[] credential, int credentialType, int requestedQuality, int userId) {
         Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId);
         final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid(
                 getGateKeeperService(), credentialHash, credential, userId);
@@ -2459,10 +2476,10 @@
         } else {
             clearUserKeyProtection(userId);
             setKeystorePassword(null, userId);
-            getGateKeeperService().clearSecureUserId(userId);
+            gateKeeperClearSecureUserId(userId);
         }
         fixateNewestUserKeyAuth(userId);
-        setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, handle, userId);
+        setSyntheticPasswordHandleLocked(handle, userId);
         return auth;
     }
 
@@ -2471,6 +2488,14 @@
                 SyntheticPasswordManager.DEFAULT_HANDLE, userId);
     }
 
+    private void setSyntheticPasswordHandleLocked(long handle, int userId) {
+        final long oldHandle = getSyntheticPasswordHandleLocked(userId);
+        setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, handle, userId);
+        setLong(PREV_SYNTHETIC_PASSWORD_HANDLE_KEY, oldHandle, userId);
+        setLong(SYNTHETIC_PASSWORD_UPDATE_TIME_KEY, System.currentTimeMillis(), userId);
+
+    }
+
     private boolean isSyntheticPasswordBasedCredentialLocked(int userId) {
         if (userId == USER_FRP) {
             final int type = mStorage.readPersistentDataBlock().type;
@@ -2499,7 +2524,7 @@
     private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential,
             @CredentialType int credentialType, @ChallengeType int challengeType, long challenge,
             int userId, ICheckCredentialProgressCallback progressCallback,
-            @Nullable ArrayList<PendingResetLockout> resetLockouts) throws RemoteException {
+            @Nullable ArrayList<PendingResetLockout> resetLockouts) {
 
         final boolean hasEnrolledBiometrics = mInjector.hasEnrolledBiometrics(userId);
 
@@ -2606,7 +2631,7 @@
     @GuardedBy("mSpManager")
     private long setLockCredentialWithAuthTokenLocked(byte[] credential,
             @CredentialType int credentialType, AuthenticationToken auth, int requestedQuality,
-            int userId) throws RemoteException {
+            int userId) {
         if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId);
         long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(),
                 credential, credentialType, auth, requestedQuality, userId);
@@ -2638,7 +2663,7 @@
 
             // we are clearing password of a secured device, so need to nuke SID as well.
             mSpManager.clearSidForUser(userId);
-            getGateKeeperService().clearSecureUserId(userId);
+            gateKeeperClearSecureUserId(userId);
             // Clear key from vold so ActivityManager can just unlock the user with empty secret
             // during boot. Vold storage needs to be unlocked before manipulation of the keys can
             // succeed.
@@ -2648,7 +2673,7 @@
             unlockKeystore(auth.deriveKeyStorePassword(), userId);
             setKeystorePassword(null, userId);
         }
-        setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, newHandle, userId);
+        setSyntheticPasswordHandleLocked(newHandle, userId);
         synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
 
         notifyActivePasswordMetricsAvailable(credentialType, credential, userId);
@@ -2665,7 +2690,7 @@
     @GuardedBy("mSpManager")
     private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
             byte[] savedCredential, int requestedQuality, int userId,
-            boolean allowUntrustedChange, boolean isLockTiedToParent) throws RemoteException {
+            boolean allowUntrustedChange, boolean isLockTiedToParent) {
         if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
         if (isManagedProfileWithUnifiedLock(userId)) {
             // get credential from keystore when managed profile has unified lock
@@ -2688,9 +2713,8 @@
 
         // If existing credential is provided, the existing credential must match.
         if (savedCredential != null && auth == null) {
-            throw new IllegalStateException("Failed to enroll "
-                    + (credentialType == CREDENTIAL_TYPE_PASSWORD
-                    ? "password" : "pattern"));
+            throw new IllegalStateException(String.format("Failed to enroll %s",
+                    credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
         }
         boolean untrustedReset = false;
         if (auth != null) {
@@ -2741,7 +2765,7 @@
      * If user is a managed profile with unified challenge, currentCredential is ignored.
      */
     @Override
-    public byte[] getHashFactor(byte[] currentCredential, int userId) throws RemoteException {
+    public byte[] getHashFactor(byte[] currentCredential, int userId) {
         checkPasswordReadPermission(userId);
         if (currentCredential == null || currentCredential.length == 0) {
             currentCredential = null;
@@ -2770,8 +2794,7 @@
         }
     }
 
-    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback)
-            throws RemoteException {
+    private long addEscrowToken(byte[] token, int userId, EscrowTokenStateChangeCallback callback) {
         if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId);
         synchronized (mSpManager) {
             enableSyntheticPasswordLocked();
@@ -2847,7 +2870,7 @@
     }
 
     private boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle,
-            byte[] token, int requestedQuality, int userId) throws RemoteException {
+            byte[] token, int requestedQuality, int userId) {
         boolean result;
         synchronized (mSpManager) {
             if (!mSpManager.hasEscrowData(userId)) {
@@ -2874,8 +2897,7 @@
 
     @GuardedBy("mSpManager")
     private boolean setLockCredentialWithTokenInternalLocked(byte[] credential, int type,
-            long tokenHandle, byte[] token, int requestedQuality, int userId)
-                    throws RemoteException {
+            long tokenHandle, byte[] token, int requestedQuality, int userId) {
         final AuthenticationResult result;
         result = mSpManager.unwrapTokenBasedSyntheticPassword(
                 getGateKeeperService(), tokenHandle, token, userId);
@@ -2901,8 +2923,7 @@
         return true;
     }
 
-    private boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId)
-            throws RemoteException {
+    private boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId) {
         AuthenticationResult authResult;
         synchronized (mSpManager) {
             if (!mSpManager.hasEscrowData(userId)) {
@@ -2920,29 +2941,57 @@
         return true;
     }
 
+    static String timestampToString(long timestamp) {
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timestamp));
+    }
+
     @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args){
-        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+    protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return;
+        IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
 
         pw.println("Current lock settings service state:");
+
         pw.println(String.format("SP Enabled = %b",
                 mLockPatternUtils.isSyntheticPasswordEnabled()));
+        pw.println();
 
+        pw.println("User State:");
+        pw.increaseIndent();
         List<UserInfo> users = mUserManager.getUsers();
         for (int user = 0; user < users.size(); user++) {
             final int userId = users.get(user).id;
-            pw.println("    User " + userId);
+            pw.println("User " + userId);
+            pw.increaseIndent();
             synchronized (mSpManager) {
-                pw.println(String.format("        SP Handle = %x",
+                pw.println(String.format("SP Handle: %x",
                         getSyntheticPasswordHandleLocked(userId)));
+                pw.println(String.format("Last changed: %s (%x)",
+                        timestampToString(getLong(SYNTHETIC_PASSWORD_UPDATE_TIME_KEY, 0, userId)),
+                        getLong(PREV_SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId)));
             }
             try {
-                pw.println(String.format("        SID = %x",
+                pw.println(String.format("SID: %x",
                         getGateKeeperService().getSecureUserId(userId)));
             } catch (RemoteException e) {
                 // ignore.
             }
+            // It's OK to dump the password type since anyone with physical access can just
+            // observe it from the keyguard directly.
+            pw.println("PasswordType: " + getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 0, userId));
+            pw.println("hasPassword: " + havePassword(userId));
+            pw.println("hasPattern: " + havePattern(userId)); // print raw credential type instead?
+            pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabled(userId));
+            pw.decreaseIndent();
         }
+        pw.println();
+        pw.decreaseIndent();
+
+        pw.println("Storage:");
+        pw.increaseIndent();
+        mStorage.dump(pw);
+        pw.println();
+        pw.decreaseIndent();
     }
 
     private void disableEscrowTokenOnNonManagedDevicesIfNeeded(int userId) {
@@ -3081,11 +3130,7 @@
         @Override
         public long addEscrowToken(byte[] token, int userId,
                 EscrowTokenStateChangeCallback callback) {
-            try {
-                return LockSettingsService.this.addEscrowToken(token, userId, callback);
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
+            return LockSettingsService.this.addEscrowToken(token, userId, callback);
         }
 
         @Override
@@ -3105,21 +3150,13 @@
                 throw new UnsupportedOperationException(
                         "This operation requires secure lock screen feature.");
             }
-            try {
-                return LockSettingsService.this.setLockCredentialWithToken(credential, type,
-                        tokenHandle, token, requestedQuality, userId);
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
+            return LockSettingsService.this.setLockCredentialWithToken(credential, type,
+                    tokenHandle, token, requestedQuality, userId);
         }
 
         @Override
         public boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId) {
-            try {
-                return LockSettingsService.this.unlockUserWithToken(tokenHandle, token, userId);
-            } catch (RemoteException re) {
-                throw re.rethrowFromSystemServer();
-            }
+            return LockSettingsService.this.unlockUserWithToken(tokenHandle, token, userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 29b8aa2..fe12a94 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.CredentialType;
@@ -140,7 +141,7 @@
                 dos.close();
                 return os.toByteArray();
             } catch (IOException e) {
-                throw new RuntimeException(e);
+                throw new IllegalStateException("Fail to serialze credential hash", e);
             }
         }
 
@@ -157,7 +158,7 @@
                 }
                 return new CredentialHash(hash, type);
             } catch (IOException e) {
-                throw new RuntimeException(e);
+                throw new IllegalStateException("Fail to deserialze credential hash", e);
             }
         }
     }
@@ -666,7 +667,7 @@
                 dos.writeInt(qualityForUi);
                 dos.write(payload);
             } catch (IOException e) {
-                throw new RuntimeException("ByteArrayOutputStream cannot throw IOException");
+                throw new IllegalStateException("ByteArrayOutputStream cannot throw IOException");
             }
             return os.toByteArray();
         }
@@ -676,6 +677,26 @@
         void initialize(SQLiteDatabase db);
     }
 
+    public void dump(IndentingPrintWriter pw) {
+        final UserManager um = UserManager.get(mContext);
+        for (UserInfo user : um.getUsers(false)) {
+            File userPath = getSyntheticPasswordDirectoryForUser(user.id);
+            pw.println(String.format("User %d [%s]:", user.id, userPath.getAbsolutePath()));
+            pw.increaseIndent();
+            File[] files = userPath.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    pw.println(String.format("%4d %s %s", file.length(),
+                            LockSettingsService.timestampToString(file.lastModified()),
+                            file.getName()));
+                }
+            } else {
+                pw.println("[Not found]");
+            }
+            pw.decreaseIndent();
+        }
+    }
+
     static class DatabaseHelper extends SQLiteOpenHelper {
         private static final String TAG = "LockSettingsDB";
         private static final String DATABASE_NAME = "locksettings.db";
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 4ef63c0..17aca15 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -109,7 +109,7 @@
     public void markSlotInUse(int slot) throws RuntimeException {
         ensureSlotMapLoaded();
         if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) {
-            throw new RuntimeException("password slot " + slot + " is not available");
+            throw new IllegalStateException("password slot " + slot + " is not available");
         }
         mSlotMap.put(slot, getMode());
         saveSlotMap();
@@ -123,7 +123,7 @@
     public void markSlotDeleted(int slot) throws RuntimeException {
         ensureSlotMapLoaded();
         if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) {
-            throw new RuntimeException("password slot " + slot + " cannot be deleted");
+            throw new IllegalStateException("password slot " + slot + " cannot be deleted");
         }
         mSlotMap.remove(slot);
         saveSlotMap();
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index 388e51f..ea0fb47 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -18,6 +18,7 @@
 
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.util.Slog;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -43,6 +44,7 @@
 import javax.crypto.spec.SecretKeySpec;
 
 public class SyntheticPasswordCrypto {
+    private static final String TAG = "SyntheticPasswordCrypto";
     private static final int PROFILE_KEY_IV_SIZE = 12;
     private static final int DEFAULT_TAG_LENGTH_BITS = 128;
     private static final int AES_KEY_LENGTH = 32; // 256-bit AES key
@@ -80,12 +82,12 @@
         byte[] ciphertext = cipher.doFinal(blob);
         byte[] iv = cipher.getIV();
         if (iv.length != PROFILE_KEY_IV_SIZE) {
-            throw new RuntimeException("Invalid iv length: " + iv.length);
+            throw new IllegalArgumentException("Invalid iv length: " + iv.length);
         }
         final GCMParameterSpec spec = cipher.getParameters().getParameterSpec(
                 GCMParameterSpec.class);
         if (spec.getTLen() != DEFAULT_TAG_LENGTH_BITS) {
-            throw new RuntimeException("Invalid tag length: " + spec.getTLen());
+            throw new IllegalArgumentException("Invalid tag length: " + spec.getTLen());
         }
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         outputStream.write(iv);
@@ -102,7 +104,7 @@
         } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
                 | IllegalBlockSizeException | BadPaddingException | IOException
                 | InvalidParameterSpecException e) {
-            e.printStackTrace();
+            Slog.e(TAG, "Failed to encrypt", e);
             return null;
         }
     }
@@ -116,7 +118,7 @@
         } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
                 | IllegalBlockSizeException | BadPaddingException
                 | InvalidAlgorithmParameterException e) {
-            e.printStackTrace();
+            Slog.e(TAG, "Failed to decrypt", e);
             return null;
         }
     }
@@ -130,8 +132,8 @@
             byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
             return decrypt(decryptionKey, intermediate);
         } catch (Exception e) {
-            e.printStackTrace();
-            throw new RuntimeException("Failed to decrypt blob", e);
+            Slog.e(TAG, "Failed to decrypt V1 blob", e);
+            throw new IllegalStateException("Failed to decrypt blob", e);
         }
     }
 
@@ -148,8 +150,8 @@
                 | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
                 | InvalidKeyException | UnrecoverableKeyException
                 | InvalidAlgorithmParameterException e) {
-            e.printStackTrace();
-            throw new RuntimeException("Failed to decrypt blob", e);
+            Slog.e(TAG, "Failed to decrypt blob", e);
+            throw new IllegalStateException("Failed to decrypt blob", e);
         }
     }
 
@@ -180,8 +182,8 @@
                 | KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
                 | InvalidKeyException
                 | InvalidParameterSpecException e) {
-            e.printStackTrace();
-            throw new RuntimeException("Failed to encrypt blob", e);
+            Slog.e(TAG, "Failed to create blob", e);
+            throw new IllegalStateException("Failed to encrypt blob", e);
         }
     }
 
@@ -193,7 +195,7 @@
             keyStore.deleteEntry(keyAlias);
         } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
                 | IOException e) {
-            e.printStackTrace();
+            Slog.e(TAG, "Failed to destroy blob", e);
         }
     }
 
@@ -202,7 +204,7 @@
             final int PADDING_LENGTH = 128;
             MessageDigest digest = MessageDigest.getInstance("SHA-512");
             if (personalisation.length > PADDING_LENGTH) {
-                throw new RuntimeException("Personalisation too long");
+                throw new IllegalArgumentException("Personalisation too long");
             }
             // Personalize the hash
             // Pad it to the block size of the hash function
@@ -213,7 +215,7 @@
             }
             return digest.digest();
         } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("NoSuchAlgorithmException for SHA-512", e);
+            throw new IllegalStateException("NoSuchAlgorithmException for SHA-512", e);
         }
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 1ba0e8c..955a9aa 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -349,25 +349,28 @@
      * a default all-zero key is used. If the value is not specified, a fresh random secret is
      * generated as the value.
      *
-     * @return the value stored in the weaver slot
-     * @throws RemoteException
+     * @return the value stored in the weaver slot, or null if the operation fails
      */
-    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value)
-            throws RemoteException {
+    private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
-            throw new RuntimeException("Invalid slot for weaver");
+            throw new IllegalArgumentException("Invalid slot for weaver");
         }
         if (key == null) {
             key = new byte[mWeaverConfig.keySize];
         } else if (key.length != mWeaverConfig.keySize) {
-            throw new RuntimeException("Invalid key size for weaver");
+            throw new IllegalArgumentException("Invalid key size for weaver");
         }
         if (value == null) {
             value = secureRandom(mWeaverConfig.valueSize);
         }
-        int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
-        if (writeStatus != WeaverStatus.OK) {
-            Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
+        try {
+            int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value));
+            if (writeStatus != WeaverStatus.OK) {
+                Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus);
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "weaver write failed", e);
             return null;
         }
         return value;
@@ -377,47 +380,53 @@
      * Verify the supplied key against a weaver slot, returning a response indicating whether
      * the verification is successful, throttled or failed. If successful, the bound secret
      * is also returned.
-     * @throws RemoteException
      */
-    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) throws RemoteException {
+    private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
         if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
-            throw new RuntimeException("Invalid slot for weaver");
+            throw new IllegalArgumentException("Invalid slot for weaver");
         }
         if (key == null) {
             key = new byte[mWeaverConfig.keySize];
         } else if (key.length != mWeaverConfig.keySize) {
-            throw new RuntimeException("Invalid key size for weaver");
+            throw new IllegalArgumentException("Invalid key size for weaver");
         }
         final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
-        mWeaver.read(slot, toByteArrayList(key), (int status, WeaverReadResponse readResponse) -> {
-            switch (status) {
-                case WeaverReadStatus.OK:
-                    response[0] = new VerifyCredentialResponse(
-                            fromByteArrayList(readResponse.value));
-                    break;
-                case WeaverReadStatus.THROTTLE:
-                    response[0] = new VerifyCredentialResponse(readResponse.timeout);
-                    Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
-                    break;
-                case WeaverReadStatus.INCORRECT_KEY:
-                    if (readResponse.timeout == 0) {
-                        response[0] = VerifyCredentialResponse.ERROR;
-                        Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
-                    } else {
-                        response[0] = new VerifyCredentialResponse(readResponse.timeout);
-                        Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot);
+        try {
+            mWeaver.read(slot, toByteArrayList(key),
+                    (int status, WeaverReadResponse readResponse) -> {
+                    switch (status) {
+                        case WeaverReadStatus.OK:
+                            response[0] = new VerifyCredentialResponse(
+                                    fromByteArrayList(readResponse.value));
+                            break;
+                        case WeaverReadStatus.THROTTLE:
+                            response[0] = new VerifyCredentialResponse(readResponse.timeout);
+                            Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot);
+                            break;
+                        case WeaverReadStatus.INCORRECT_KEY:
+                            if (readResponse.timeout == 0) {
+                                response[0] = VerifyCredentialResponse.ERROR;
+                                Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot);
+                            } else {
+                                response[0] = new VerifyCredentialResponse(readResponse.timeout);
+                                Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: "
+                                        + slot);
+                            }
+                            break;
+                        case WeaverReadStatus.FAILED:
+                            response[0] = VerifyCredentialResponse.ERROR;
+                            Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
+                            break;
+                        default:
+                            response[0] = VerifyCredentialResponse.ERROR;
+                            Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
+                            break;
                     }
-                    break;
-                case WeaverReadStatus.FAILED:
-                    response[0] = VerifyCredentialResponse.ERROR;
-                    Log.e(TAG, "weaver read failed (FAILED), slot: " + slot);
-                    break;
-               default:
-                   response[0] = VerifyCredentialResponse.ERROR;
-                   Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot);
-                   break;
-            }
-        });
+                });
+        } catch (RemoteException e) {
+            response[0] = VerifyCredentialResponse.ERROR;
+            Log.e(TAG, "weaver read failed, slot: " + slot, e);
+        }
         return response[0];
     }
 
@@ -460,12 +469,15 @@
      *
      */
     public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper,
-            byte[] hash, byte[] credential, int userId) throws RemoteException {
+            byte[] hash, byte[] credential, int userId) {
         AuthenticationToken result = AuthenticationToken.create();
         GateKeeperResponse response;
         if (hash != null) {
-            response = gatekeeper.enroll(userId, hash, credential,
-                    result.deriveGkPassword());
+            try {
+                response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword());
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Failed to enroll credential duing SP init", e);
+            }
             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
                 Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId);
                 clearSidForUser(userId);
@@ -484,9 +496,13 @@
      * Used when adding password to previously-unsecured devices.
      */
     public void newSidForUser(IGateKeeperService gatekeeper, AuthenticationToken authToken,
-            int userId) throws RemoteException {
-        GateKeeperResponse response = gatekeeper.enroll(userId, null, null,
-                authToken.deriveGkPassword());
+            int userId) {
+        GateKeeperResponse response;
+        try {
+            response = gatekeeper.enroll(userId, null, null, authToken.deriveGkPassword());
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Failed to create new SID for user", e);
+        }
         if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
             Log.e(TAG, "Fail to create new SID for user " + userId);
             return;
@@ -565,12 +581,8 @@
             Set<Integer> usedSlots = getUsedWeaverSlots();
             if (!usedSlots.contains(slot)) {
                 Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
-                try {
-                    weaverEnroll(slot, null, null);
-                    mPasswordSlotManager.markSlotDeleted(slot);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to destroy slot", e);
-                }
+                weaverEnroll(slot, null, null);
+                mPasswordSlotManager.markSlotDeleted(slot);
             } else {
                 Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
             }
@@ -608,7 +620,7 @@
                 return i;
             }
         }
-        throw new RuntimeException("Run out of weaver slots.");
+        throw new IllegalStateException("Run out of weaver slots.");
     }
 
     /**
@@ -622,11 +634,12 @@
      *
      * @see #newSidForUser
      * @see #clearSidForUser
+     * @return a new password handle for the wrapped SP blob
+     * @throw IllegalStateException if creation fails.
      */
     public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
             byte[] credential, int credentialType, AuthenticationToken authToken,
-            int requestedQuality, int userId)
-                    throws RemoteException {
+            int requestedQuality, int userId) {
         if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) {
             credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE;
             credential = DEFAULT_PASSWORD;
@@ -642,10 +655,11 @@
             // Weaver based user password
             int weaverSlot = getNextAvailableWeaverSlot();
             Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
-            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
+            byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
+                    null);
             if (weaverSecret == null) {
-                Log.e(TAG, "Fail to enroll user password under weaver " + userId);
-                return DEFAULT_HANDLE;
+                throw new IllegalStateException(
+                        "Fail to enroll user password under weaver " + userId);
             }
             saveWeaverSlot(weaverSlot, handle, userId);
             mPasswordSlotManager.markSlotInUse(weaverSlot);
@@ -657,13 +671,22 @@
         } else {
             // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
             // to prevent them from accumulating and causing problems.
-            gatekeeper.clearSecureUserId(fakeUid(userId));
+            try {
+                gatekeeper.clearSecureUserId(fakeUid(userId));
+            } catch (RemoteException ignore) {
+                Log.w(TAG, "Failed to clear SID from gatekeeper");
+            }
             // GateKeeper based user password
-            GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
-                    passwordTokenToGkInput(pwdToken));
+            GateKeeperResponse response;
+            try {
+                response = gatekeeper.enroll(fakeUid(userId), null, null,
+                        passwordTokenToGkInput(pwdToken));
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Failed to enroll password for new SP blob", e);
+            }
             if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
-                Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
-                return DEFAULT_HANDLE;
+                throw new IllegalStateException(
+                        "Fail to enroll user password when creating SP for user " + userId);
             }
             pwd.passwordHandle = response.getPayload();
             sid = sidFromPasswordHandle(pwd.passwordHandle);
@@ -680,14 +703,20 @@
 
     public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper,
             byte[] userCredential, int credentialType,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+            ICheckCredentialProgressCallback progressCallback) {
         PersistentData persistentData = mStorage.readPersistentDataBlock();
         if (persistentData.type == PersistentData.TYPE_SP) {
             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
             byte[] pwdToken = computePasswordToken(userCredential, pwd);
 
-            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
-                    0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+            GateKeeperResponse response;
+            try {
+                response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
+                        0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+            } catch (RemoteException e) {
+                Log.e(TAG, "FRP verifyChallenge failed", e);
+                return VerifyCredentialResponse.ERROR;
+            }
             return VerifyCredentialResponse.fromGateKeeperResponse(response);
         } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) {
             PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
@@ -805,11 +834,9 @@
         }
         if (isWeaverAvailable()) {
             int slot = getNextAvailableWeaverSlot();
-            try {
-                Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
-                weaverEnroll(slot, null, tokenData.weaverSecret);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
+            Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
+            if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) {
+                Log.e(TAG, "Failed to enroll weaver secret when activating token");
                 return false;
             }
             saveWeaverSlot(slot, handle, userId);
@@ -859,7 +886,7 @@
      */
     public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper,
             long handle, byte[] credential, int userId,
-            ICheckCredentialProgressCallback progressCallback) throws RemoteException {
+            ICheckCredentialProgressCallback progressCallback) {
         if (credential == null) {
             credential = DEFAULT_PASSWORD;
         }
@@ -886,14 +913,28 @@
             applicationId = transformUnderWeaverSecret(pwdToken, result.gkResponse.getPayload());
         } else {
             byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
-            GateKeeperResponse response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
-                    pwd.passwordHandle, gkPwdToken);
+            GateKeeperResponse response;
+            try {
+                response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
+                        pwd.passwordHandle, gkPwdToken);
+            } catch (RemoteException e) {
+                Log.e(TAG, "gatekeeper verify failed", e);
+                result.gkResponse = VerifyCredentialResponse.ERROR;
+                return result;
+            }
             int responseCode = response.getResponseCode();
             if (responseCode == GateKeeperResponse.RESPONSE_OK) {
                 result.gkResponse = VerifyCredentialResponse.OK;
                 if (response.getShouldReEnroll()) {
-                    GateKeeperResponse reenrollResponse = gatekeeper.enroll(fakeUid(userId),
-                            pwd.passwordHandle, gkPwdToken, gkPwdToken);
+                    GateKeeperResponse reenrollResponse;
+                    try {
+                        reenrollResponse = gatekeeper.enroll(fakeUid(userId),
+                                pwd.passwordHandle, gkPwdToken, gkPwdToken);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "Fail to invoke gatekeeper.enroll", e);
+                        reenrollResponse = GateKeeperResponse.ERROR;
+                        // continue the flow anyway
+                    }
                     if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                         pwd.passwordHandle = reenrollResponse.getPayload();
                         saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId);
@@ -922,7 +963,11 @@
         // Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
         // Notify the callback so the keyguard UI can proceed immediately.
         if (progressCallback != null) {
-            progressCallback.onCredentialVerified();
+            try {
+                progressCallback.onCredentialVerified();
+            } catch (RemoteException e) {
+                Log.w(TAG, "progressCallback throws exception", e);
+            }
         }
         result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED,
                 applicationId, sid, userId);
@@ -938,8 +983,7 @@
      * verification to referesh the SID & Auth token maintained by the system.
      */
     public @NonNull AuthenticationResult unwrapTokenBasedSyntheticPassword(
-            IGateKeeperService gatekeeper, long handle, byte[] token, int userId)
-                    throws RemoteException {
+            IGateKeeperService gatekeeper, long handle, byte[] token, int userId) {
         AuthenticationResult result = new AuthenticationResult();
         byte[] secdiscardable = loadSecdiscardable(handle, userId);
         int slotId = loadWeaverSlot(handle, userId);
@@ -985,10 +1029,10 @@
         if (version != SYNTHETIC_PASSWORD_VERSION_V3
                 && version != SYNTHETIC_PASSWORD_VERSION_V2
                 && version != SYNTHETIC_PASSWORD_VERSION_V1) {
-            throw new RuntimeException("Unknown blob version");
+            throw new IllegalArgumentException("Unknown blob version");
         }
         if (blob[1] != type) {
-            throw new RuntimeException("Invalid blob type");
+            throw new IllegalArgumentException("Invalid blob type");
         }
         final byte[] secret;
         if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
@@ -1028,38 +1072,48 @@
      * decrypt SP.
      */
     public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
-            @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
+            @NonNull AuthenticationToken auth, long challenge, int userId) {
         byte[] spHandle = loadSyntheticPasswordHandle(userId);
         if (spHandle == null) {
             // There is no password handle associated with the given user, i.e. the user is not
             // secured by lockscreen and has no SID, so just return here;
             return null;
         }
-        VerifyCredentialResponse result;
-        GateKeeperResponse response = gatekeeper.verifyChallenge(userId, challenge,
-                spHandle, auth.deriveGkPassword());
+        GateKeeperResponse response;
+        try {
+            response = gatekeeper.verifyChallenge(userId, challenge,
+                    spHandle, auth.deriveGkPassword());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Fail to verify with gatekeeper " + userId, e);
+            return VerifyCredentialResponse.ERROR;
+        }
         int responseCode = response.getResponseCode();
         if (responseCode == GateKeeperResponse.RESPONSE_OK) {
-            result = new VerifyCredentialResponse(response.getPayload());
+            VerifyCredentialResponse result = new VerifyCredentialResponse(response.getPayload());
             if (response.getShouldReEnroll()) {
-                response = gatekeeper.enroll(userId, spHandle,
-                        spHandle, auth.deriveGkPassword());
+                try {
+                    response = gatekeeper.enroll(userId, spHandle, spHandle,
+                            auth.deriveGkPassword());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to invoke gatekeeper.enroll", e);
+                    response = GateKeeperResponse.ERROR;
+                }
                 if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) {
                     spHandle = response.getPayload();
                     saveSyntheticPasswordHandle(spHandle, userId);
                     // Call self again to re-verify with updated handle
                     return verifyChallenge(gatekeeper, auth, challenge, userId);
                 } else {
+                    // Fall through, return result from the previous verification attempt.
                     Log.w(TAG, "Fail to re-enroll SP handle for user " + userId);
-                    // Fall through, return existing handle
                 }
             }
+            return result;
         } else if (responseCode == GateKeeperResponse.RESPONSE_RETRY) {
-            result = new VerifyCredentialResponse(response.getTimeout());
+            return new VerifyCredentialResponse(response.getTimeout());
         } else {
-            result = VerifyCredentialResponse.ERROR;
+            return VerifyCredentialResponse.ERROR;
         }
-        return result;
     }
 
     public boolean existsHandle(long handle, int userId) {
@@ -1183,7 +1237,7 @@
     private byte[] passwordTokenToWeaverKey(byte[] token) {
         byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
         if (key.length < mWeaverConfig.keySize) {
-            throw new RuntimeException("weaver key length too small");
+            throw new IllegalArgumentException("weaver key length too small");
         }
         return Arrays.copyOf(key, mWeaverConfig.keySize);
     }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index c05655a..193e0af 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -1912,16 +1912,17 @@
                     && AudioSystem.isStreamActive(suggestedStream, 0)) {
                 preferSuggestedStream = true;
             }
-            if (DEBUG_KEY_EVENT) {
-                Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
-                        + flags + ", suggestedStream=" + suggestedStream
-                        + ", preferSuggestedStream=" + preferSuggestedStream);
-            }
             if (session == null || preferSuggestedStream) {
+                if (DEBUG_KEY_EVENT) {
+                    Log.d(TAG, "Adjusting suggestedStream=" + suggestedStream + " by " + direction
+                            + ". flags=" + flags + ", preferSuggestedStream="
+                            + preferSuggestedStream + ", session=" + session);
+                }
                 if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
                         && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
-                    if (DEBUG) {
-                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
+                    if (DEBUG_KEY_EVENT) {
+                        Log.d(TAG, "Nothing is playing on the music stream. Skipping volume event,"
+                                + " flags=" + flags);
                     }
                     return;
                 }
@@ -1955,6 +1956,11 @@
                     }
                 });
             } else {
+                if (DEBUG_KEY_EVENT) {
+                    Log.d(TAG, "Adjusting " + session + " by " + direction + ". flags="
+                            + flags + ", suggestedStream=" + suggestedStream
+                            + ", preferSuggestedStream=" + preferSuggestedStream);
+                }
                 session.adjustVolume(packageName, opPackageName, pid, uid, null, asSystemService,
                         direction, flags, true);
             }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6fe924e..e90db38 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -231,7 +231,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.TriPredicate;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.EventLogTags;
 import com.android.server.IoThread;
 import com.android.server.LocalServices;
@@ -621,6 +621,8 @@
                 mConditionProviders.readXml(
                         parser, mAllowedManagedServicePackages, forRestore, userId);
                 migratedManagedServices = true;
+            } else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {
+                mSnoozeHelper.readXml(parser);
             }
             if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
                 if (forRestore && userId != UserHandle.USER_SYSTEM) {
@@ -709,6 +711,7 @@
         mPreferencesHelper.writeXml(out, forBackup, userId);
         mListeners.writeXml(out, forBackup, userId);
         mAssistants.writeXml(out, forBackup, userId);
+        mSnoozeHelper.writeXml(out);
         mConditionProviders.writeXml(out, forBackup, userId);
         if (!forBackup || userId == UserHandle.USER_SYSTEM) {
             writeSecureNotificationsPolicy(out);
@@ -1753,6 +1756,7 @@
                 com.android.internal.R.integer.config_notificationWarnRemoteViewSizeBytes);
         mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
+
     }
 
     @Override
@@ -4830,7 +4834,7 @@
                 final ActivityManagerInternal am = LocalServices
                         .getService(ActivityManagerInternal.class);
                 final long duration = LocalServices.getService(
-                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
+                        DeviceIdleInternal.class).getNotificationWhitelistDuration();
                 for (int i = 0; i < intentCount; i++) {
                     PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                     if (pendingIntent != null) {
@@ -5284,7 +5288,7 @@
             updateLightsLocked();
             if (mSnoozeCriterionId != null) {
                 mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
-                mSnoozeHelper.snooze(r);
+                mSnoozeHelper.snooze(r, mSnoozeCriterionId);
             } else {
                 mSnoozeHelper.snooze(r, mDuration);
             }
@@ -5387,6 +5391,27 @@
         @Override
         public void run() {
             synchronized (mNotificationLock) {
+                final Long snoozeAt =
+                        mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                                r.getUser().getIdentifier(),
+                                r.sbn.getPackageName(), r.sbn.getKey());
+                final long currentTime = System.currentTimeMillis();
+                if (snoozeAt.longValue() > currentTime) {
+                    (new SnoozeNotificationRunnable(r.sbn.getKey(),
+                            snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
+                    return;
+                }
+
+                final String contextId =
+                        mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+                                r.getUser().getIdentifier(),
+                                r.sbn.getPackageName(), r.sbn.getKey());
+                if (contextId != null) {
+                    (new SnoozeNotificationRunnable(r.sbn.getKey(),
+                            0, contextId)).snoozeLocked(r);
+                    return;
+                }
+
                 mEnqueuedNotifications.add(r);
                 scheduleTimeoutLocked(r);
 
@@ -6937,6 +6962,7 @@
         if (DBG) {
             Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
         }
+        mSnoozeHelper.cleanupPersistedContext(key);
         mSnoozeHelper.repost(key);
         handleSavePolicyFile();
     }
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 91f497c..8125d0d 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -55,6 +55,21 @@
  * NotificationManagerService helper for handling snoozed notifications.
  */
 public class SnoozeHelper {
+    public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1";
+
+    protected static final String XML_TAG_NAME = "snoozed-notifications";
+
+    private static final String XML_SNOOZED_NOTIFICATION = "notification";
+    private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+    private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg";
+    private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id";
+    private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+    //the time the snoozed notification should be reposted
+    private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+    private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+    private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
+
     private static final String TAG = "SnoozeHelper";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final String INDENT = "    ";
@@ -72,6 +87,17 @@
     // User id : package name : notification key : record.
     private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
             mSnoozedNotifications = new ArrayMap<>();
+    // User id : package name : notification key : time-milliseconds .
+    // This member stores persisted snoozed notification trigger times. it persists through reboots
+    // It should have the notifications that haven't expired or re-posted yet
+    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>>
+            mPersistedSnoozedNotifications = new ArrayMap<>();
+    // User id : package name : notification key : creation ID .
+    // This member stores persisted snoozed notification trigger context for the assistant
+    // it persists through reboots.
+    // It should have the notifications that haven't expired or re-posted yet
+    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>>
+            mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
     // notification key : package.
     private ArrayMap<String, String> mPackages = new ArrayMap<>();
     // key : userId
@@ -89,6 +115,34 @@
         mUserProfiles = userProfiles;
     }
 
+    void cleanupPersistedContext(String key){
+        int userId = mUsers.get(key);
+        String pkg = mPackages.get(key);
+        synchronized (mPersistedSnoozedNotificationsWithContext) {
+            removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+        }
+    }
+
+    //This function has a side effect of removing the time from the list of persisted notifications.
+    //IT IS NOT IDEMPOTENT!
+    @NonNull
+    protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
+        Long time;
+        synchronized (mPersistedSnoozedNotifications) {
+            time = removeRecord(pkg, key, userId, mPersistedSnoozedNotifications);
+        }
+        if (time == null) {
+            return 0L;
+        }
+        return time;
+    }
+
+    protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
+        synchronized (mPersistedSnoozedNotificationsWithContext) {
+            return removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+        }
+    }
+
     protected boolean isSnoozed(int userId, String pkg, String key) {
         return mSnoozedNotifications.containsKey(userId)
                 && mSnoozedNotifications.get(userId).containsKey(pkg)
@@ -169,32 +223,82 @@
      * Snoozes a notification and schedules an alarm to repost at that time.
      */
     protected void snooze(NotificationRecord record, long duration) {
+        String pkg = record.sbn.getPackageName();
+        String key = record.getKey();
+        int userId = record.getUser().getIdentifier();
+
         snooze(record);
-        scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
+        scheduleRepost(pkg, key, userId, duration);
+        Long activateAt = System.currentTimeMillis() + duration;
+        synchronized (mPersistedSnoozedNotifications) {
+            storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
+        }
     }
 
     /**
      * Records a snoozed notification.
      */
-    protected void snooze(NotificationRecord record) {
+    protected void snooze(NotificationRecord record, String contextId) {
+        int userId = record.getUser().getIdentifier();
+        if (contextId != null) {
+            synchronized (mPersistedSnoozedNotificationsWithContext) {
+                storeRecord(record.sbn.getPackageName(), record.getKey(),
+                        userId, mPersistedSnoozedNotificationsWithContext, contextId);
+            }
+        }
+        snooze(record);
+    }
+
+    private void snooze(NotificationRecord record) {
         int userId = record.getUser().getIdentifier();
         if (DEBUG) {
             Slog.d(TAG, "Snoozing " + record.getKey());
         }
-        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
-                mSnoozedNotifications.get(userId);
+        storeRecord(record.sbn.getPackageName(), record.getKey(),
+                userId, mSnoozedNotifications, record);
+        mPackages.put(record.getKey(), record.sbn.getPackageName());
+        mUsers.put(record.getKey(), userId);
+    }
+
+    private <T> void storeRecord(String pkg, String key, Integer userId,
+            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) {
+
+        ArrayMap<String, ArrayMap<String, T>> records =
+                targets.get(userId);
         if (records == null) {
             records = new ArrayMap<>();
         }
-        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
+        ArrayMap<String, T> pkgRecords = records.get(pkg);
         if (pkgRecords == null) {
             pkgRecords = new ArrayMap<>();
         }
-        pkgRecords.put(record.getKey(), record);
-        records.put(record.sbn.getPackageName(), pkgRecords);
-        mSnoozedNotifications.put(userId, records);
-        mPackages.put(record.getKey(), record.sbn.getPackageName());
-        mUsers.put(record.getKey(), userId);
+        pkgRecords.put(key, object);
+        records.put(pkg, pkgRecords);
+        targets.put(userId, records);
+
+    }
+
+    private <T> T removeRecord(String pkg, String key, Integer userId,
+            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) {
+        T object = null;
+
+        ArrayMap<String, ArrayMap<String, T>> records =
+                targets.get(userId);
+        if (records == null) {
+            return null;
+        }
+        ArrayMap<String, T> pkgRecords = records.get(pkg);
+        if (pkgRecords == null) {
+            return null;
+        }
+        object = pkgRecords.remove(key);
+        if (pkgRecords.size() == 0) {
+            records.remove(pkg);
+        }
+        if (records.size() == 0) {
+            targets.remove(userId);
+        }
+        return object;
     }
 
     protected boolean cancel(int userId, String pkg, String tag, int id) {
@@ -414,13 +518,121 @@
         }
     }
 
-    protected void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
-
+    protected void writeXml(XmlSerializer out) throws IOException {
+        final long currentTime = System.currentTimeMillis();
+        out.startTag(null, XML_TAG_NAME);
+        writeXml(out, mPersistedSnoozedNotifications, XML_SNOOZED_NOTIFICATION,
+                value -> {
+                    if (value < currentTime) {
+                        return;
+                    }
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
+                            value.toString());
+                });
+        writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT,
+                value -> {
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID,
+                            value);
+                });
+        out.endTag(null, XML_TAG_NAME);
     }
 
-    public void readXml(XmlPullParser parser, boolean forRestore)
-            throws XmlPullParserException, IOException {
+    private interface Inserter<T> {
+        void insert(T t) throws IOException;
+    }
+    private <T> void writeXml(XmlSerializer out,
+            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag,
+            Inserter<T> attributeInserter)
+            throws IOException {
+        synchronized (targets) {
+            final int M = targets.size();
+            for (int i = 0; i < M; i++) {
+                final ArrayMap<String, ArrayMap<String, T>> packages =
+                        targets.valueAt(i);
+                if (packages == null) {
+                    continue;
+                }
+                final int N = packages.size();
+                for (int j = 0; j < N; j++) {
+                    final ArrayMap<String, T> keyToValue = packages.valueAt(j);
+                    if (keyToValue == null) {
+                        continue;
+                    }
+                    final int O = keyToValue.size();
+                    for (int k = 0; k < O; k++) {
+                        T value = keyToValue.valueAt(k);
 
+                        out.startTag(null, tag);
+
+                        attributeInserter.insert(value);
+
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+                                XML_SNOOZED_NOTIFICATION_VERSION);
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k));
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j));
+                        out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+                                targets.keyAt(i).toString());
+
+                        out.endTag(null, tag);
+
+                    }
+                }
+            }
+        }
+    }
+
+    protected void readXml(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+            String tag = parser.getName();
+            if (type == XmlPullParser.END_TAG
+                    && XML_TAG_NAME.equals(tag)) {
+                break;
+            }
+            if (type == XmlPullParser.START_TAG
+                    && (XML_SNOOZED_NOTIFICATION.equals(tag)
+                        || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT))
+                    && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL)
+                        .equals(XML_SNOOZED_NOTIFICATION_VERSION)) {
+                try {
+                    final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
+                    final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
+                    final int userId = Integer.parseInt(
+                            parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID));
+                    if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
+                        final Long time = Long.parseLong(
+                                parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME));
+                        if (time > System.currentTimeMillis()) { //only read new stuff
+                            synchronized (mPersistedSnoozedNotifications) {
+                                storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time);
+                            }
+                            scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
+                        }
+                        continue;
+                    }
+                    if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
+                        final String creationId = parser.getAttributeValue(
+                                null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
+                        synchronized (mPersistedSnoozedNotificationsWithContext) {
+                            storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
+                                    creationId);
+                        }
+                        continue;
+                    }
+
+
+                } catch (Exception e) {
+                    //we dont cre if it is a number format exception or a null pointer exception.
+                    //we just want to debug it and continue with our lives
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                                "Exception in reading snooze data from policy xml: "
+                                        + e.getMessage());
+                    }
+                }
+            }
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 91824c3..20a4c75 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -129,11 +129,11 @@
         }
     }
 
-    static void startIdmapService() {
+    private static void startIdmapService() {
         SystemProperties.set("ctl.start", IDMAP_DAEMON);
     }
 
-    static void stopIdmapService() {
+    private static void stopIdmapService() {
         SystemProperties.set("ctl.stop", IDMAP_DAEMON);
     }
 
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ce95181..965ddc9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -262,7 +262,6 @@
 
             initIfNeeded();
             onSwitchUser(UserHandle.USER_SYSTEM);
-            IdmapDaemon.stopIdmapService();
 
             publishBinderService(Context.OVERLAY_SERVICE, mService);
             publishLocalService(OverlayManagerService.class, this);
@@ -752,7 +751,7 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             final DumpState dumpState = new DumpState();
-            dumpState.setUserId(UserHandle.getUserId(Binder.getCallingUid()));
+            dumpState.setUserId(UserHandle.USER_ALL);
 
             int opti = 0;
             while (opti < args.length) {
@@ -772,13 +771,13 @@
                     pw.println("  so the following are equivalent: mState, mstate, State, state.");
                     return;
                 } else if ("--user".equals(opt)) {
-                    opti++;
                     if (opti >= args.length) {
                         pw.println("Error: user missing argument");
                         return;
                     }
                     try {
                         dumpState.setUserId(Integer.parseInt(args[opti]));
+                        opti++;
                     } catch (NumberFormatException e) {
                         pw.println("Error: user argument is not a number: " + args[opti]);
                         return;
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index a189f7f..7eb7438 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,10 +16,10 @@
 
 package com.android.server.pm;
 
+import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+
 import android.Manifest;
 import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
@@ -35,15 +35,15 @@
 
 import com.android.internal.R;
 import com.android.server.FgThread;
+import com.android.server.compat.PlatformCompat;
 
-import java.util.ArrayList;
+import java.io.PrintWriter;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * The entity responsible for filtering visibility between apps based on declarations in their
@@ -55,27 +55,34 @@
 
     // Forces filtering logic to run for debug purposes.
     // STOPSHIP (b/136675067): should be false after development is complete
-    private static final boolean DEBUG_FILTERING = true;
+    private static final boolean DEBUG_RUN_WHEN_DISABLED = true;
+
+    // Logs all filtering instead of enforcing
+    private static final boolean DEBUG_ALLOW_ALL = false;
+
+    @SuppressWarnings("ConstantExpression")
+    private static final boolean DEBUG_LOGGING = false | DEBUG_RUN_WHEN_DISABLED | DEBUG_ALLOW_ALL;
+
     /**
      * This contains a list of packages that are implicitly queryable because another app explicitly
      * interacted with it. For example, if application A starts a service in application B,
      * application B is implicitly allowed to query for application A; regardless of any manifest
      * entries.
      */
-    private final SparseArray<HashMap<String, ArrayList<String>>> mImplicitlyQueryable =
+    private final SparseArray<HashMap<String, Set<String>>> mImplicitlyQueryable =
             new SparseArray<>();
 
     /**
      * A mapping from the set of packages that query other packages via package name to the
      * list of packages that they can see.
      */
-    private final HashMap<String, List<String>> mQueriesViaPackage = new HashMap<>();
+    private final HashMap<String, Set<String>> mQueriesViaPackage = new HashMap<>();
 
     /**
      * A mapping from the set of packages that query others via intent to the list
      * of packages that the intents resolve to.
      */
-    private final HashMap<String, List<String>> mQueriesViaIntent = new HashMap<>();
+    private final HashMap<String, Set<String>> mQueriesViaIntent = new HashMap<>();
 
     /**
      * A set of packages that are always queryable by any package, regardless of their manifest
@@ -93,14 +100,11 @@
 
     private final IPermissionManager mPermissionManager;
 
-    private final AppOpsManager mAppOpsManager;
-    private final ConfigProvider mConfigProvider;
+    private final FeatureConfig mFeatureConfig;
 
-    AppsFilter(ConfigProvider configProvider, IPermissionManager permissionManager,
-            AppOpsManager appOpsManager, String[] forceQueryableWhitelist,
-            boolean systemAppsQueryable) {
-        mConfigProvider = configProvider;
-        mAppOpsManager = appOpsManager;
+    AppsFilter(FeatureConfig featureConfig, IPermissionManager permissionManager,
+            String[] forceQueryableWhitelist, boolean systemAppsQueryable) {
+        mFeatureConfig = featureConfig;
         final HashSet<String> forceQueryableByDeviceSet = new HashSet<>();
         Collections.addAll(forceQueryableByDeviceSet, forceQueryableWhitelist);
         this.mForceQueryableByDevice = Collections.unmodifiableSet(forceQueryableByDeviceSet);
@@ -109,39 +113,83 @@
         mSystemAppsQueryable = systemAppsQueryable;
     }
 
-    public static AppsFilter create(Context context) {
-        // tracks whether the feature is enabled where -1 is unknown, 0 is false and 1 is true;
-        final AtomicInteger featureEnabled = new AtomicInteger(-1);
+    public interface FeatureConfig {
+        /** Called when the system is ready and components can be queried. */
+        void onSystemReady();
 
+        /** @return true if we should filter apps at all. */
+        boolean isGloballyEnabled();
+
+        /** @return true if the feature is enabled for the given package. */
+        boolean packageIsEnabled(PackageParser.Package pkg);
+    }
+
+    private static class FeatureConfigImpl implements FeatureConfig {
+        private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
+
+        // STOPSHIP(patb): set this to true if we plan to launch this in R
+        private static final boolean DEFAULT_ENABLED_STATE = false;
+        private final PackageManagerService.Injector mInjector;
+        private volatile boolean mFeatureEnabled = DEFAULT_ENABLED_STATE;
+
+        private FeatureConfigImpl(PackageManagerService.Injector injector) {
+            mInjector = injector;
+        }
+
+        @Override
+        public void onSystemReady() {
+            mFeatureEnabled = DeviceConfig.getBoolean(
+                    NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
+                    DEFAULT_ENABLED_STATE);
+            DeviceConfig.addOnPropertiesChangedListener(
+                    NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
+                    properties -> {
+                        synchronized (FeatureConfigImpl.this) {
+                            mFeatureEnabled = properties.getBoolean(
+                                    FILTERING_ENABLED_NAME, DEFAULT_ENABLED_STATE);
+                        }
+                    });
+        }
+
+        @Override
+        public boolean isGloballyEnabled() {
+            return mFeatureEnabled;
+        }
+
+        @Override
+        public boolean packageIsEnabled(PackageParser.Package pkg) {
+            final PlatformCompat compatibility = mInjector.getCompatibility();
+            if (compatibility == null) {
+                Slog.wtf(TAG, "PlatformCompat is null");
+                return mFeatureEnabled;
+            }
+            return compatibility.isChangeEnabled(
+                    PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo);
+        }
+    }
+
+
+    public static AppsFilter create(PackageManagerService.Injector injector) {
         final boolean forceSystemAppsQueryable =
-                context.getResources().getBoolean(R.bool.config_forceSystemPackagesQueryable);
+                injector.getContext().getResources()
+                        .getBoolean(R.bool.config_forceSystemPackagesQueryable);
+        final FeatureConfig featureConfig = new FeatureConfigImpl(injector);
         final String[] forcedQueryablePackageNames;
         if (forceSystemAppsQueryable) {
             // all system apps already queryable, no need to read and parse individual exceptions
             forcedQueryablePackageNames = new String[]{};
         } else {
             forcedQueryablePackageNames =
-                    context.getResources().getStringArray(R.array.config_forceQueryablePackages);
+                    injector.getContext().getResources()
+                            .getStringArray(R.array.config_forceQueryablePackages);
             for (int i = 0; i < forcedQueryablePackageNames.length; i++) {
                 forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
             }
         }
         IPermissionManager permissionmgr =
                 (IPermissionManager) ServiceManager.getService("permissionmgr");
-        return new AppsFilter(() -> {
-            if (featureEnabled.get() < 0) {
-                featureEnabled.set(DeviceConfig.getBoolean(
-                        DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
-                        "package_query_filtering_enabled", false) ? 1 : 0);
-                DeviceConfig.addOnPropertiesChangedListener(
-                        DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE,
-                        FgThread.getExecutor(),
-                        pr -> featureEnabled.set(
-                                pr.getBoolean("package_query_filtering_enabled", false) ? 1 : 0));
-            }
-            return featureEnabled.get() == 1;
-        }, permissionmgr,
-                context.getSystemService(AppOpsManager.class), forcedQueryablePackageNames,
+
+        return new AppsFilter(featureConfig, permissionmgr, forcedQueryablePackageNames,
                 forceSystemAppsQueryable);
     }
 
@@ -182,17 +230,21 @@
      */
     private void markAppInteraction(
             PackageSetting initiatingPackage, PackageSetting targetPackage, int userId) {
-        HashMap<String, ArrayList<String>> currentUser = mImplicitlyQueryable.get(userId);
+        HashMap<String, Set<String>> currentUser = mImplicitlyQueryable.get(userId);
         if (currentUser == null) {
             currentUser = new HashMap<>();
             mImplicitlyQueryable.put(userId, currentUser);
         }
         if (!currentUser.containsKey(targetPackage.pkg.packageName)) {
-            currentUser.put(targetPackage.pkg.packageName, new ArrayList<>());
+            currentUser.put(targetPackage.pkg.packageName, new HashSet<>());
         }
         currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName);
     }
 
+    public void onSystemReady() {
+        mFeatureConfig.onSystemReady();
+    }
+
     /**
      * Adds a package that should be considered when filtering visibility between apps.
      *
@@ -217,7 +269,7 @@
             }
         }
         // if the new package declares them, let's evaluate its ability to see existing packages
-        mQueriesViaIntent.put(newPkg.packageName, new ArrayList<>());
+        mQueriesViaIntent.put(newPkg.packageName, new HashSet<>());
         for (PackageParser.Package existingPackage : existing.values()) {
             if (existingPackage.packageName == newPkg.packageName) {
                 continue;
@@ -231,7 +283,7 @@
                 mQueriesViaIntent.get(newPkg.packageName).add(existingPackage.packageName);
             }
         }
-        final ArrayList<String> queriesPackages = new ArrayList<>(
+        final HashSet<String> queriesPackages = new HashSet<>(
                 newPkg.mQueriesPackages == null ? 0 : newPkg.mQueriesPackages.size());
         if (newPkg.mQueriesPackages != null) {
             queriesPackages.addAll(newPkg.mQueriesPackages);
@@ -249,13 +301,13 @@
 
         for (int i = 0; i < mImplicitlyQueryable.size(); i++) {
             mImplicitlyQueryable.valueAt(i).remove(packageName);
-            for (ArrayList<String> initiators : mImplicitlyQueryable.valueAt(i).values()) {
+            for (Set<String> initiators : mImplicitlyQueryable.valueAt(i).values()) {
                 initiators.remove(packageName);
             }
         }
 
         mQueriesViaIntent.remove(packageName);
-        for (List<String> declarators : mQueriesViaIntent.values()) {
+        for (Set<String> declarators : mQueriesViaIntent.values()) {
             declarators.remove(packageName);
         }
 
@@ -274,11 +326,11 @@
      */
     public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
             PackageSetting targetPkgSetting, int userId) {
-        if (callingUid < Process.FIRST_APPLICATION_UID) {
+        final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
+        if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) {
             return false;
         }
-        final boolean featureEnabled = mConfigProvider.isEnabled();
-        if (!featureEnabled && !DEBUG_FILTERING) {
+        if (callingUid < Process.FIRST_APPLICATION_UID) {
             return false;
         }
         if (callingSetting == null) {
@@ -319,29 +371,21 @@
                 return true;
             }
         }
-
         if (!featureEnabled) {
             return false;
         }
-        final int mode = mAppOpsManager
-                .checkOpNoThrow(AppOpsManager.OP_QUERY_ALL_PACKAGES, callingUid,
-                        callingPkgSetting.pkg.packageName);
-        switch (mode) {
-            case AppOpsManager.MODE_DEFAULT:
-                // if default, let's rely on remote feature toggle to determine whether to
-                // actually filter
-                return true;
-            case AppOpsManager.MODE_ALLOWED:
-                // explicitly allowed to see all packages, don't filter
-                return false;
-            case AppOpsManager.MODE_ERRORED:
-                // deny / error: let's log so developer can audit usages
-                Slog.i(TAG, callingPkgSetting.pkg.packageName
-                        + " blocked from accessing " + targetPkgSetting.pkg.packageName);
-            case AppOpsManager.MODE_IGNORED:
-                // fall through
-            default:
-                return true;
+        if (mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
+            if (DEBUG_LOGGING) {
+                Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
+                        + targetPkgSetting.name + (DEBUG_ALLOW_ALL ? " ALLOWED" : "BLOCKED"));
+            }
+            return !DEBUG_ALLOW_ALL;
+        } else {
+            if (DEBUG_LOGGING) {
+                Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
+                        + targetPkgSetting.name + " DISABLED");
+            }
+            return false;
         }
     }
 
@@ -382,6 +426,13 @@
                 && mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) {
             return false;
         }
+        if (callingPkgSetting.pkg.instrumentation.size() > 0) {
+            for (int i = 0, max = callingPkgSetting.pkg.instrumentation.size(); i < max; i++) {
+                if (callingPkgSetting.pkg.instrumentation.get(i).info.targetPackage == targetName) {
+                    return false;
+                }
+            }
+        }
         try {
             if (mPermissionManager.checkPermission(
                     Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId)
@@ -399,8 +450,51 @@
                 || mForceQueryableByDevice.contains(targetPkgSetting.pkg.packageName));
     }
 
-    public interface ConfigProvider {
-        boolean isEnabled();
+    public void dumpQueries(
+            PrintWriter pw, @Nullable String filteringPackageName, DumpState dumpState,
+            int[] users) {
+        pw.println();
+        pw.println("Queries:");
+        dumpState.onTitlePrinted();
+        pw.println("  system apps queryable: " + mSystemAppsQueryable);
+        dumpPackageSet(pw, filteringPackageName, mForceQueryableByDevice, "System whitelist", "  ");
+        dumpPackageSet(pw, filteringPackageName, mForceQueryable, "forceQueryable", "  ");
+        pw.println("  queries via package name:");
+        dumpQueriesMap(pw, filteringPackageName, mQueriesViaPackage, "    ");
+        pw.println("  queries via intent:");
+        dumpQueriesMap(pw, filteringPackageName, mQueriesViaIntent, "    ");
+        pw.println("  queryable via interaction:");
+        for (int user : users) {
+            pw.append("    User ").append(Integer.toString(user)).println(":");
+            final HashMap<String, Set<String>> queryMapForUser = mImplicitlyQueryable.get(user);
+            dumpQueriesMap(pw, filteringPackageName, queryMapForUser, "      ");
+        }
     }
 
+    private static void dumpQueriesMap(PrintWriter pw, @Nullable String filteringPackageName,
+            HashMap<String, Set<String>> queriesMap, String spacing) {
+        for (String callingPkg : queriesMap.keySet()) {
+            if (Objects.equals(callingPkg, filteringPackageName)) {
+                // don't filter target package names if the calling is filteringPackageName
+                dumpPackageSet(pw, null /*filteringPackageName*/, queriesMap.get(callingPkg),
+                        callingPkg, spacing);
+            } else {
+                dumpPackageSet(pw, filteringPackageName, queriesMap.get(callingPkg), callingPkg,
+                        spacing);
+            }
+        }
+    }
+
+    private static void dumpPackageSet(PrintWriter pw, @Nullable String filteringPackageName,
+            Set<String> targetPkgSet, String subTitle, String spacing) {
+        if (targetPkgSet != null && targetPkgSet.size() > 0
+                && (filteringPackageName == null || targetPkgSet.contains(filteringPackageName))) {
+            pw.append(spacing).append(subTitle).println(":");
+            for (String pkgName : targetPkgSet) {
+                if (filteringPackageName == null || Objects.equals(filteringPackageName, pkgName)) {
+                    pw.append(spacing).append("  ").println(pkgName);
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index d473fbf..99a1093 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -42,6 +42,7 @@
     public static final int DUMP_VOLUMES = 1 << 23;
     public static final int DUMP_SERVICE_PERMISSIONS = 1 << 24;
     public static final int DUMP_APEX = 1 << 25;
+    public static final int DUMP_QUERIES = 1 << 26;
 
     public static final int OPTION_SHOW_FILTERS = 1 << 0;
     public static final int OPTION_DUMP_ALL_COMPONENTS = 1 << 1;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0032e9a..3aeb2b1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1184,9 +1184,26 @@
         synchronized (mSessions) {
             pw.println("Active install sessions:");
             pw.increaseIndent();
+
+            List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
             int N = mSessions.size();
             for (int i = 0; i < N; i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
+                if (session.isStagedAndInTerminalState()) {
+                    finalizedSessions.add(session);
+                    continue;
+                }
+                session.dump(pw);
+                pw.println();
+            }
+            pw.println();
+            pw.decreaseIndent();
+
+            pw.println("Finalized install sessions:");
+            pw.increaseIndent();
+            N = finalizedSessions.size();
+            for (int i = 0; i < N; i++) {
+                final PackageInstallerSession session = finalizedSessions.get(i);
                 session.dump(pw);
                 pw.println();
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eddb930..b720290 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2337,6 +2337,7 @@
         pw.printPair("mInstallerPackageName", mInstallerPackageName);
         pw.printPair("mInstallerUid", mInstallerUid);
         pw.printPair("createdMillis", createdMillis);
+        pw.printPair("updatedMillis", updatedMillis);
         pw.printPair("stageDir", stageDir);
         pw.printPair("stageCid", stageCid);
         pw.println();
@@ -2356,6 +2357,13 @@
         pw.printPair("mFinalMessage", mFinalMessage);
         pw.printPair("params.isMultiPackage", params.isMultiPackage);
         pw.printPair("params.isStaged", params.isStaged);
+        pw.printPair("mParentSessionId", mParentSessionId);
+        pw.printPair("mChildSessionIds", mChildSessionIds);
+        pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
+        pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
+        pw.printPair("mStagedSessionReady", mStagedSessionReady);
+        pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode);
+        pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage);
         pw.println();
 
         pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3f6d4df..15a3a72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -290,7 +290,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.AttributeCache;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -300,6 +300,7 @@
 import com.android.server.SystemConfig;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.Watchdog;
+import com.android.server.compat.PlatformCompat;
 import com.android.server.net.NetworkPolicyManagerInternal;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.Settings.DatabaseVersion;
@@ -816,7 +817,7 @@
         private final Singleton<UserManagerService> mUserManagerProducer;
         private final Singleton<Settings> mSettingsProducer;
         private final Singleton<ActivityTaskManagerInternal> mActivityTaskManagerProducer;
-        private final Singleton<DeviceIdleController.LocalService> mLocalDeviceIdleController;
+        private final Singleton<DeviceIdleInternal> mLocalDeviceIdleController;
         private final Singleton<StorageManagerInternal> mStorageManagerInternalProducer;
         private final Singleton<NetworkPolicyManagerInternal> mNetworkPolicyManagerProducer;
         private final Singleton<PermissionPolicyInternal> mPermissionPolicyProducer;
@@ -824,6 +825,8 @@
         private final Singleton<DisplayManager> mDisplayManagerProducer;
         private final Singleton<StorageManager> mStorageManagerProducer;
         private final Singleton<AppOpsManager> mAppOpsManagerProducer;
+        private final Singleton<AppsFilter> mAppsFilterProducer;
+        private final Singleton<PlatformCompat> mPlatformCompatProducer;
 
         Injector(Context context, Object lock, Installer installer,
                 Object installLock, PackageAbiHelper abiHelper,
@@ -832,14 +835,16 @@
                 Producer<UserManagerService> userManagerProducer,
                 Producer<Settings> settingsProducer,
                 Producer<ActivityTaskManagerInternal> activityTaskManagerProducer,
-                Producer<DeviceIdleController.LocalService> deviceIdleControllerProducer,
+                Producer<DeviceIdleInternal> deviceIdleControllerProducer,
                 Producer<StorageManagerInternal> storageManagerInternalProducer,
                 Producer<NetworkPolicyManagerInternal> networkPolicyManagerProducer,
                 Producer<PermissionPolicyInternal> permissionPolicyProvider,
                 Producer<DeviceStorageMonitorInternal> deviceStorageMonitorProducer,
                 Producer<DisplayManager> displayManagerProducer,
                 Producer<StorageManager> storageManagerProducer,
-                Producer<AppOpsManager> appOpsManagerProducer) {
+                Producer<AppOpsManager> appOpsManagerProducer,
+                Producer<AppsFilter> appsFilterProducer,
+                Producer<PlatformCompat> platformCompatProducer) {
             mContext = context;
             mLock = lock;
             mInstaller = installer;
@@ -858,6 +863,8 @@
             mDisplayManagerProducer = new Singleton<>(displayManagerProducer);
             mStorageManagerProducer = new Singleton<>(storageManagerProducer);
             mAppOpsManagerProducer = new Singleton<>(appOpsManagerProducer);
+            mAppsFilterProducer = new Singleton<>(appsFilterProducer);
+            mPlatformCompatProducer = new Singleton<>(platformCompatProducer);
         }
 
         /**
@@ -912,7 +919,7 @@
             return mActivityTaskManagerProducer.get(this, mPackageManager);
         }
 
-        public DeviceIdleController.LocalService getLocalDeviceIdleController() {
+        public DeviceIdleInternal getLocalDeviceIdleController() {
             return mLocalDeviceIdleController.get(this, mPackageManager);
         }
 
@@ -943,6 +950,14 @@
         public AppOpsManager getAppOpsManager() {
             return mAppOpsManagerProducer.get(this, mPackageManager);
         }
+
+        public AppsFilter getAppsFilter() {
+            return mAppsFilterProducer.get(this, mPackageManager);
+        }
+
+        public PlatformCompat getCompatibility() {
+            return mPlatformCompatProducer.get(this, mPackageManager);
+        }
     }
 
     private final AppsFilter mAppsFilter;
@@ -1249,7 +1264,7 @@
             final BroadcastOptions options = BroadcastOptions.makeBasic();
             options.setTemporaryAppWhitelistDuration(whitelistTimeout);
 
-            DeviceIdleController.LocalService idleController =
+            DeviceIdleInternal idleController =
                     mInjector.getLocalDeviceIdleController();
             idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
                     mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout,
@@ -2273,9 +2288,9 @@
      * @param packageVolume The storage volume of the package.
      * @param packageIsExternal true if the package is currently installed on
      * external/removable/unprotected storage.
-     * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the
-     * corresponding {@link StorageEnum} storage type value if it is.
-     * corresponding {@link StorageEnum} storage type value if it is.
+     * @return {@link StorageEnums#UNKNOWN} if the package is not stored externally or the
+     * corresponding {@link StorageEnums} storage type value if it is.
+     * corresponding {@link StorageEnums} storage type value if it is.
      */
     private static int getPackageExternalStorageType(VolumeInfo packageVolume,
             boolean packageIsExternal) {
@@ -2425,14 +2440,16 @@
                                 i.getPermissionManagerServiceInternal().getPermissionSettings(),
                                 lock),
                 new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class),
-                new Injector.LocalServicesProducer<>(DeviceIdleController.LocalService.class),
+                new Injector.LocalServicesProducer<>(DeviceIdleInternal.class),
                 new Injector.LocalServicesProducer<>(StorageManagerInternal.class),
                 new Injector.LocalServicesProducer<>(NetworkPolicyManagerInternal.class),
                 new Injector.LocalServicesProducer<>(PermissionPolicyInternal.class),
                 new Injector.LocalServicesProducer<>(DeviceStorageMonitorInternal.class),
                 new Injector.SystemServiceProducer<>(DisplayManager.class),
                 new Injector.SystemServiceProducer<>(StorageManager.class),
-                new Injector.SystemServiceProducer<>(AppOpsManager.class));
+                new Injector.SystemServiceProducer<>(AppOpsManager.class),
+                (i, pm) -> AppsFilter.create(i),
+                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
 
         PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
         t.traceEnd(); // "create package manager"
@@ -2617,7 +2634,7 @@
         mProtectedPackages = new ProtectedPackages(mContext);
 
         mApexManager = ApexManager.create(mContext);
-        mAppsFilter = AppsFilter.create(mContext);
+        mAppsFilter = mInjector.getAppsFilter();
 
         // CHECKSTYLE:OFF IndentationCheck
         synchronized (mInstallLock) {
@@ -2725,14 +2742,10 @@
             mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
             mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
 
-            int preUpgradeSdkVersion = ver.sdkVersion;
-
             // save off the names of pre-existing system packages prior to scanning; we don't
             // want to automatically grant runtime permissions for new system apps
             if (mPromoteSystemApps) {
-                Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
-                while (pkgSettingIter.hasNext()) {
-                    PackageSetting ps = pkgSettingIter.next();
+                for (PackageSetting ps : mSettings.mPackages.values()) {
                     if (isSystemApp(ps)) {
                         mExistingSystemPackages.add(ps.name);
                     }
@@ -14511,7 +14524,7 @@
                     final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
                             receivers, verificationState);
 
-                    DeviceIdleController.LocalService idleController =
+                    DeviceIdleInternal idleController =
                             mInjector.getLocalDeviceIdleController();
                     final long idleDuration = getVerificationTimeout();
 
@@ -14580,17 +14593,6 @@
                             TRACE_TAG_PACKAGE_MANAGER, "enable_rollback", enableRollbackToken);
                     mPendingEnableRollback.append(enableRollbackToken, this);
 
-                    final int[] installedUsers;
-                    synchronized (mLock) {
-                        PackageSetting ps = mSettings.getPackageLPr(pkgLite.packageName);
-                        if (ps != null) {
-                            installedUsers = ps.queryInstalledUsers(mUserManager.getUserIds(),
-                                    true);
-                        } else {
-                            installedUsers = new int[0];
-                        }
-                    }
-
                     Intent enableRollbackIntent = new Intent(Intent.ACTION_PACKAGE_ENABLE_ROLLBACK);
                     enableRollbackIntent.putExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN,
@@ -14599,9 +14601,6 @@
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS,
                             installFlags);
                     enableRollbackIntent.putExtra(
-                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS,
-                            installedUsers);
-                    enableRollbackIntent.putExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER,
                             getRollbackUser().getIdentifier());
                     enableRollbackIntent.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
@@ -20459,6 +20458,8 @@
                         .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
         co.onChange(true);
 
+        mAppsFilter.onSystemReady();
+
         // Disable any carrier apps. We do this very early in boot to prevent the apps from being
         // disabled after already being started.
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
@@ -20663,6 +20664,7 @@
                 pw.println("    preferred-xml [--full]: print preferred package settings as xml");
                 pw.println("    prov[iders]: dump content providers");
                 pw.println("    p[ackages]: dump installed packages");
+                pw.println("    q[ueries]: dump app queryability calculations");
                 pw.println("    s[hared-users]: dump shared user IDs");
                 pw.println("    m[essages]: print collected runtime messages");
                 pw.println("    v[erifiers]: print package verifier info");
@@ -20785,6 +20787,8 @@
                 dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
             } else if ("p".equals(cmd) || "packages".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_PACKAGES);
+            } else if ("q".equals(cmd) || "queries".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_QUERIES);
             } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_SHARED_USERS);
                 if (opti < args.length && "noperm".equals(args[opti])) {
@@ -21079,6 +21083,10 @@
                 mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
             }
 
+            if (dumpState.isDumping(DumpState.DUMP_QUERIES)) {
+                mAppsFilter.dumpQueries(pw, packageName, dumpState, mUserManager.getUserIds());
+            }
+
             if (dumpState.isDumping(DumpState.DUMP_SHARED_USERS)) {
                 mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
             }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index c1cb53d..5972468 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4570,6 +4570,13 @@
                 pw.print(prefix); pw.print("  privateFlags="); printFlags(pw,
                         ps.pkg.applicationInfo.privateFlags, PRIVATE_FLAG_DUMP_SPEC); pw.println();
             }
+            pw.print(prefix); pw.print("  forceQueryable="); pw.println(ps.pkg.mForceQueryable);
+            if (ps.pkg.mQueriesPackages != null) {
+                pw.append(prefix).append("  queriesPackages=").println(ps.pkg.mQueriesPackages);
+            }
+            if (ps.pkg.mQueriesIntents != null) {
+                pw.append(prefix).append("  queriesIntents=").println(ps.pkg.mQueriesIntents);
+            }
             pw.print(prefix); pw.print("  dataDir="); pw.println(ps.pkg.applicationInfo.dataDir);
             pw.print(prefix); pw.print("  supportsScreens=[");
             boolean first = true;
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 6d3424c..dd1eb83 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -35,7 +35,6 @@
 import android.content.pm.PackageParser.SigningDetails;
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.ParceledListSlice;
-import android.content.pm.Signature;
 import android.content.rollback.IRollbackManager;
 import android.os.Bundle;
 import android.os.Handler;
@@ -106,8 +105,19 @@
         return new ParceledListSlice<>(result);
     }
 
-    private void validateApexSignature(String apexPath, String packageName)
+    /**
+     * Validates the signature used to sign the container of the new apex package
+     *
+     * @param newApexPkg The new apex package that is being installed
+     * @param installFlags flags related to the session
+     * @throws PackageManagerException
+     */
+    private void validateApexSignature(PackageInfo newApexPkg, int installFlags)
             throws PackageManagerException {
+        // Get signing details of the new package
+        final String apexPath = newApexPkg.applicationInfo.sourceDir;
+        final String packageName = newApexPkg.packageName;
+
         final SigningDetails signingDetails;
         try {
             signingDetails = ApkSignatureVerifier.verify(apexPath, SignatureSchemeVersion.JAR);
@@ -116,9 +126,10 @@
                     "Failed to parse APEX package " + apexPath, e);
         }
 
-        final PackageInfo packageInfo = mApexManager.getPackageInfo(packageName,
+        // Get signing details of the existing package
+        final PackageInfo existingApexPkg = mApexManager.getPackageInfo(packageName,
                 ApexManager.MATCH_ACTIVE_PACKAGE);
-        if (packageInfo == null) {
+        if (existingApexPkg == null) {
             // This should never happen, because submitSessionToApexService ensures that no new
             // apexes were installed.
             throw new IllegalStateException("Unknown apex package " + packageName);
@@ -127,22 +138,22 @@
         final SigningDetails existingSigningDetails;
         try {
             existingSigningDetails = ApkSignatureVerifier.verify(
-                packageInfo.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
+                existingApexPkg.applicationInfo.sourceDir, SignatureSchemeVersion.JAR);
         } catch (PackageParserException e) {
             throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                    "Failed to parse APEX package " + packageInfo.applicationInfo.sourceDir, e);
+                    "Failed to parse APEX package " + existingApexPkg.applicationInfo.sourceDir, e);
         }
 
-        // Now that we have both sets of signatures, demand that they're an exact match.
-        if (Signature.areExactMatch(existingSigningDetails.signatures, signingDetails.signatures)) {
+        // Verify signing details for upgrade
+        if (signingDetails.checkCapability(existingSigningDetails,
+                PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA)) {
             return;
         }
 
         throw new PackageManagerException(SessionInfo.STAGED_SESSION_VERIFICATION_FAILED,
-                "APK-container signature verification failed for package "
-                        + packageName + ". Signature of file "
-                        + apexPath + " does not match the signature of "
-                        + " the package already installed.");
+                "APK-container signature of APEX package " + packageName + " with version "
+                        + newApexPkg.versionCodeMajor + " and path " + apexPath + " is not"
+                        + " compatible with the one currently installed on device");
     }
 
     private List<PackageInfo> submitSessionToApexService(
@@ -239,8 +250,7 @@
             try {
                 final List<PackageInfo> apexPackages = submitSessionToApexService(session);
                 for (PackageInfo apexPackage : apexPackages) {
-                    validateApexSignature(apexPackage.applicationInfo.sourceDir,
-                            apexPackage.packageName);
+                    validateApexSignature(apexPackage, session.params.installFlags);
                 }
             } catch (PackageManagerException e) {
                 session.setStagedSessionFailed(e.error, e.getMessage());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 2ccb6c1..1fe5512 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2252,14 +2252,10 @@
 
     @GuardedBy({"mPackagesLock", "mRestrictionsLock"})
     private void fallbackToSingleUserLP() {
-        int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED;
-        // In split system user mode, the admin and primary flags are assigned to the first human
-        // user.
-        if (!UserManager.isSplitSystemUser()) {
-            flags |= UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY;
-        }
+        int flags = UserInfo.FLAG_SYSTEM | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN;
+        // In headless system user mode, the primary flag is assigned to the first human user.
         if (!UserManager.isHeadlessSystemUserMode()) {
-            flags |= UserInfo.FLAG_FULL;
+            flags |= UserInfo.FLAG_PRIMARY | UserInfo.FLAG_FULL;
         }
         // Create the system user
         UserInfo system = new UserInfo(UserHandle.USER_SYSTEM, null, null, flags);
@@ -2773,9 +2769,9 @@
                         return null;
                     }
                 }
-                // In split system user mode, we assign the first human user the primary flag.
+                // In headless system user mode, we assign the first human user the primary flag.
                 // And if there is no device owner, we also assign the admin flag to primary user.
-                if (UserManager.isSplitSystemUser()
+                if (UserManager.isHeadlessSystemUserMode()
                         && !isGuest && !isManagedProfile && getPrimaryUser() == null) {
                     flags |= UserInfo.FLAG_PRIMARY;
                     synchronized (mUsersLock) {
@@ -3762,6 +3758,8 @@
                         pw.print(" <partial>");
                     }
                     pw.println();
+                    pw.print("    Flags: "); pw.print(userInfo.flags); pw.print(" (");
+                    pw.print(UserInfo.flagsToString(userInfo.flags)); pw.println(")");
                     pw.print("    State: ");
                     final int state;
                     synchronized (mUserStates) {
@@ -3846,6 +3844,8 @@
         pw.println("  All guests ephemeral: " + Resources.getSystem().getBoolean(
                 com.android.internal.R.bool.config_guestUserEphemeral));
         pw.println("  Is split-system user: " + UserManager.isSplitSystemUser());
+        pw.println("  Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
+        pw.println("  User version: " + mUserVersion);
     }
 
     private static void dumpTimeAgo(PrintWriter pw, StringBuilder sb, long nowTime, long time) {
@@ -4172,6 +4172,14 @@
             Bundle restrictions = getEffectiveUserRestrictions(userId);
             return restrictions != null && restrictions.getBoolean(restrictionKey);
         }
+
+        public @Nullable UserInfo getUserInfo(@UserIdInt int userId) {
+            UserData userData;
+            synchronized (mUsersLock) {
+                userData = mUsers.get(userId);
+            }
+            return userData == null ? null : userData.info;
+        }
     }
 
     /* Remove all the users except of the system one. */
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index af94e44..c0d71ac 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -62,5 +62,10 @@
                 }
             ]
         }
+    ],
+    "imports": [
+        {
+            "path": "vendor/xts/gts-tests/tests/permission"
+        }
     ]
 }
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index f5f7d67..cae09ea3 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -52,12 +52,13 @@
     }
 
     /**
-     * Creates an app data snapshot for a specified {@code packageRollbackInfo}. Updates said {@code
-     * packageRollbackInfo} with the inodes of the CE user data snapshot folders.
+     * Creates an app data snapshot for a specified {@code packageRollbackInfo} and the specified
+     * {@code userIds}. Updates said {@code packageRollbackInfo} with the inodes of the CE user data
+     * snapshot folders.
      */
-    public void snapshotAppData(int snapshotId, PackageRollbackInfo packageRollbackInfo) {
-        final int[] installedUsers = packageRollbackInfo.getInstalledUsers().toArray();
-        for (int user : installedUsers) {
+    public void snapshotAppData(
+            int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
+        for (int user : userIds) {
             final int storageFlags;
             if (isUserCredentialLocked(user)) {
                 // We've encountered a user that hasn't unlocked on a FBE device, so we can't copy
@@ -80,6 +81,7 @@
                         + packageRollbackInfo.getPackageName() + ", userId: " + user, ie);
             }
         }
+        packageRollbackInfo.getSnapshottedUsers().addAll(IntArray.wrap(userIds));
     }
 
     /**
@@ -96,14 +98,14 @@
 
         final IntArray pendingBackups = packageRollbackInfo.getPendingBackups();
         final List<RestoreInfo> pendingRestores = packageRollbackInfo.getPendingRestores();
-        boolean changedRollbackData = false;
+        boolean changedRollback = false;
 
         // If we still have a userdata backup pending for this user, it implies that the user
         // hasn't unlocked their device between the point of backup and the point of restore,
         // so the data cannot have changed. We simply skip restoring CE data in this case.
         if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) {
             pendingBackups.remove(pendingBackups.indexOf(userId));
-            changedRollbackData = true;
+            changedRollback = true;
         } else {
             // There's no pending CE backup for this user, which means that we successfully
             // managed to backup data for the user, which means we seek to restore it
@@ -111,7 +113,7 @@
                 // We've encountered a user that hasn't unlocked on a FBE device, so we can't
                 // copy across app user data until the user unlocks their device.
                 pendingRestores.add(new RestoreInfo(userId, appId, seInfo));
-                changedRollbackData = true;
+                changedRollback = true;
             } else {
                 // This user has unlocked, we can proceed to restore both CE and DE data.
                 storageFlags = storageFlags | Installer.FLAG_STORAGE_CE;
@@ -126,7 +128,7 @@
                         + packageRollbackInfo.getPackageName(), ie);
         }
 
-        return changedRollbackData;
+        return changedRollback;
     }
 
     /**
@@ -158,29 +160,29 @@
      * Packages pending backup for the given user are added to {@code pendingBackupPackages} along
      * with their corresponding {@code PackageRollbackInfo}.
      *
-     * @return the list of {@code RollbackData} that has pending backups. Note that some of the
+     * @return the list of rollbacks that have pending backups. Note that some of the
      *         backups won't be performed, because they might be counteracted by pending restores.
      */
-    private static List<RollbackData> computePendingBackups(int userId,
+    private static List<Rollback> computePendingBackups(int userId,
             Map<String, PackageRollbackInfo> pendingBackupPackages,
-            List<RollbackData> rollbacks) {
-        List<RollbackData> rd = new ArrayList<>();
+            List<Rollback> rollbacks) {
+        List<Rollback> rollbacksWithPendingBackups = new ArrayList<>();
 
-        for (RollbackData data : rollbacks) {
-            for (PackageRollbackInfo info : data.info.getPackages()) {
+        for (Rollback rollback : rollbacks) {
+            for (PackageRollbackInfo info : rollback.info.getPackages()) {
                 final IntArray pendingBackupUsers = info.getPendingBackups();
                 if (pendingBackupUsers != null) {
                     final int idx = pendingBackupUsers.indexOf(userId);
                     if (idx != -1) {
                         pendingBackupPackages.put(info.getPackageName(), info);
-                        if (rd.indexOf(data) == -1) {
-                            rd.add(data);
+                        if (rollbacksWithPendingBackups.indexOf(rollback) == -1) {
+                            rollbacksWithPendingBackups.add(rollback);
                         }
                     }
                 }
             }
         }
-        return rd;
+        return rollbacksWithPendingBackups;
     }
 
     /**
@@ -188,45 +190,45 @@
      * Packages pending restore are added to {@code pendingRestores} along with their corresponding
      * {@code PackageRollbackInfo}.
      *
-     * @return the list of {@code RollbackData} that has pending restores. Note that some of the
+     * @return the list of rollbacks that have pending restores. Note that some of the
      *         restores won't be performed, because they might be counteracted by pending backups.
      */
-    private static List<RollbackData> computePendingRestores(int userId,
+    private static List<Rollback> computePendingRestores(int userId,
             Map<String, PackageRollbackInfo> pendingRestorePackages,
-            List<RollbackData> rollbacks) {
-        List<RollbackData> rd = new ArrayList<>();
+            List<Rollback> rollbacks) {
+        List<Rollback> rollbacksWithPendingRestores = new ArrayList<>();
 
-        for (RollbackData data : rollbacks) {
-            for (PackageRollbackInfo info : data.info.getPackages()) {
+        for (Rollback rollback : rollbacks) {
+            for (PackageRollbackInfo info : rollback.info.getPackages()) {
                 final RestoreInfo ri = info.getRestoreInfo(userId);
                 if (ri != null) {
                     pendingRestorePackages.put(info.getPackageName(), info);
-                    if (rd.indexOf(data) == -1) {
-                        rd.add(data);
+                    if (rollbacksWithPendingRestores.indexOf(rollback) == -1) {
+                        rollbacksWithPendingRestores.add(rollback);
                     }
                 }
             }
         }
 
-        return rd;
+        return rollbacksWithPendingRestores;
     }
 
     /**
-     * Commits the list of pending backups and restores for a given {@code userId}. For the pending
-     * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId}
-     * to a inode of theirs CE user data snapshot.
+     * Commits the list of pending backups and restores for a given {@code userId}. For rollbacks
+     * with pending backups, updates the {@code Rollback} instance with a mapping from
+     * {@code userId} to inode of the CE user data snapshot.
      *
-     * @return the set of {@code RollbackData} that have been changed and should be stored on disk.
+     * @return the set of rollbacks with changes that should be stored on disk.
      */
-    public Set<RollbackData> commitPendingBackupAndRestoreForUser(int userId,
-            List<RollbackData> rollbacks) {
+    public Set<Rollback> commitPendingBackupAndRestoreForUser(int userId,
+            List<Rollback> rollbacks) {
 
         final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>();
-        final List<RollbackData> pendingBackups = computePendingBackups(userId,
+        final List<Rollback> pendingBackups = computePendingBackups(userId,
                 pendingBackupPackages, rollbacks);
 
         final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>();
-        final List<RollbackData> pendingRestores = computePendingRestores(userId,
+        final List<Rollback> pendingRestores = computePendingRestores(userId,
                 pendingRestorePackages, rollbacks);
 
         // First remove unnecessary backups, i.e. when user did not unlock their phone between the
@@ -246,14 +248,15 @@
         }
 
         if (!pendingBackupPackages.isEmpty()) {
-            for (RollbackData data : pendingBackups) {
-                for (PackageRollbackInfo info : data.info.getPackages()) {
+            for (Rollback rollback : pendingBackups) {
+                for (PackageRollbackInfo info : rollback.info.getPackages()) {
                     final IntArray pendingBackupUsers = info.getPendingBackups();
                     final int idx = pendingBackupUsers.indexOf(userId);
                     if (idx != -1) {
                         try {
                             long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
-                                    userId, data.info.getRollbackId(), Installer.FLAG_STORAGE_CE);
+                                    userId, rollback.info.getRollbackId(),
+                                    Installer.FLAG_STORAGE_CE);
                             info.putCeSnapshotInode(userId, ceSnapshotInode);
                             pendingBackupUsers.remove(idx);
                         } catch (InstallerException ie) {
@@ -267,13 +270,13 @@
         }
 
         if (!pendingRestorePackages.isEmpty()) {
-            for (RollbackData data : pendingRestores) {
-                for (PackageRollbackInfo info : data.info.getPackages()) {
+            for (Rollback rollback : pendingRestores) {
+                for (PackageRollbackInfo info : rollback.info.getPackages()) {
                     final RestoreInfo ri = info.getRestoreInfo(userId);
                     if (ri != null) {
                         try {
                             mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
-                                    ri.seInfo, userId, data.info.getRollbackId(),
+                                    ri.seInfo, userId, rollback.info.getRollbackId(),
                                     Installer.FLAG_STORAGE_CE);
                             info.removeRestoreInfo(ri);
                         } catch (InstallerException ie) {
@@ -285,7 +288,7 @@
             }
         }
 
-        final Set<RollbackData> changed = new HashSet<>(pendingBackups);
+        final Set<Rollback> changed = new HashSet<>(pendingBackups);
         changed.addAll(pendingRestores);
         return changed;
     }
diff --git a/services/core/java/com/android/server/rollback/RollbackData.java b/services/core/java/com/android/server/rollback/Rollback.java
similarity index 85%
rename from services/core/java/com/android/server/rollback/RollbackData.java
rename to services/core/java/com/android/server/rollback/Rollback.java
index b37e268..0d5746b 100644
--- a/services/core/java/com/android/server/rollback/RollbackData.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -32,7 +32,7 @@
  * Information about a rollback available for a set of atomically installed
  * packages.
  */
-class RollbackData {
+class Rollback {
     @IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
             ROLLBACK_STATE_ENABLING,
             ROLLBACK_STATE_AVAILABLE,
@@ -102,13 +102,13 @@
     public boolean restoreUserDataInProgress = false;
 
     /**
-     * Constructs a new, empty RollbackData instance.
+     * Constructs a new, empty Rollback instance.
      *
      * @param rollbackId the id of the rollback.
      * @param backupDir the directory where the rollback data is stored.
      * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
      */
-    RollbackData(int rollbackId, File backupDir, int stagedSessionId) {
+    Rollback(int rollbackId, File backupDir, int stagedSessionId) {
         this.info = new RollbackInfo(rollbackId,
                 /* packages */ new ArrayList<>(),
                 /* isStaged */ stagedSessionId != -1,
@@ -121,9 +121,9 @@
     }
 
     /**
-     * Constructs a RollbackData instance with full rollback data information.
+     * Constructs a pre-populated Rollback instance.
      */
-    RollbackData(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
+    Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
             @RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) {
         this.info = info;
         this.backupDir = backupDir;
@@ -143,9 +143,9 @@
 
     static String rollbackStateToString(@RollbackState int state) {
         switch (state) {
-            case RollbackData.ROLLBACK_STATE_ENABLING: return "enabling";
-            case RollbackData.ROLLBACK_STATE_AVAILABLE: return "available";
-            case RollbackData.ROLLBACK_STATE_COMMITTED: return "committed";
+            case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
+            case Rollback.ROLLBACK_STATE_AVAILABLE: return "available";
+            case Rollback.ROLLBACK_STATE_COMMITTED: return "committed";
         }
         throw new AssertionError("Invalid rollback state: " + state);
     }
@@ -153,9 +153,9 @@
     static @RollbackState int rollbackStateFromString(String state)
             throws ParseException {
         switch (state) {
-            case "enabling": return RollbackData.ROLLBACK_STATE_ENABLING;
-            case "available": return RollbackData.ROLLBACK_STATE_AVAILABLE;
-            case "committed": return RollbackData.ROLLBACK_STATE_COMMITTED;
+            case "enabling": return Rollback.ROLLBACK_STATE_ENABLING;
+            case "available": return Rollback.ROLLBACK_STATE_AVAILABLE;
+            case "committed": return Rollback.ROLLBACK_STATE_COMMITTED;
         }
         throw new ParseException("Invalid rollback state: " + state, 0);
     }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index f0cf9cf..bfd280c 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -110,13 +110,13 @@
     @GuardedBy("mLock")
     private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
 
-    // Package rollback data for rollbacks we are in the process of enabling.
+    // Rollbacks we are in the process of enabling.
     @GuardedBy("mLock")
     private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
 
     // The list of all rollbacks, including available and committed rollbacks.
     @GuardedBy("mLock")
-    private final List<RollbackData> mRollbacks;
+    private final List<Rollback> mRollbacks;
 
     private final RollbackStore mRollbackStore;
 
@@ -127,7 +127,7 @@
     private final AppDataRollbackHelper mAppDataRollbackHelper;
 
     // This field stores the difference in Millis between the uptime (millis since device
-    // has booted) and current time (device wall clock) - it's used to update rollback data
+    // has booted) and current time (device wall clock) - it's used to update rollback
     // timestamps when the time is changed, by the user or by change of timezone.
     // No need for guarding with lock because value is only accessed in handler thread.
     private long  mRelativeBootTime = calculateRelativeBootTime();
@@ -146,9 +146,9 @@
 
         // Load rollback data from device storage.
         synchronized (mLock) {
-            mRollbacks = mRollbackStore.loadAllRollbackData();
-            for (RollbackData data : mRollbacks) {
-                mAllocatedRollbackIds.put(data.info.getRollbackId(), true);
+            mRollbacks = mRollbackStore.loadRollbacks();
+            for (Rollback rollback : mRollbacks) {
+                mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
             }
         }
 
@@ -177,16 +177,14 @@
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_TOKEN, -1);
                     int installFlags = intent.getIntExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALL_FLAGS, 0);
-                    int[] installedUsers = intent.getIntArrayExtra(
-                            PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_INSTALLED_USERS);
                     int user = intent.getIntExtra(
                             PackageManagerInternal.EXTRA_ENABLE_ROLLBACK_USER, 0);
 
                     File newPackageCodePath = new File(intent.getData().getPath());
 
                     getHandler().post(() -> {
-                        boolean success = enableRollback(installFlags, newPackageCodePath,
-                                installedUsers, user, token);
+                        boolean success =
+                                enableRollback(installFlags, newPackageCodePath, user, token);
                         int ret = PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED;
                         if (!success) {
                             ret = PackageManagerInternal.ENABLE_ROLLBACK_FAILED;
@@ -273,39 +271,24 @@
         }, filter, null, getHandler());
     }
 
-    /**
-     * This method posts a blocking call to the handler thread, so it should not be called from
-     * that same thread.
-     * @throws {@link IllegalStateException} if called from {@link #mHandlerThread}
-     */
     @Override
     public ParceledListSlice getAvailableRollbacks() {
         enforceManageRollbacks("getAvailableRollbacks");
-        if (Thread.currentThread().equals(mHandlerThread)) {
-            Slog.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread "
-                    + "causes a deadlock");
-            throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks "
-                    + "from the handler thread!");
-        }
-
-        // Wait for the handler thread to get the list of available rollbacks
-        // to get the most up-to-date results. This is intended to reduce test
-        // flakiness when checking available rollbacks immediately after
-        // installing a package with rollback enabled.
-        CountDownLatch latch = new CountDownLatch(1);
-        getHandler().post(() -> latch.countDown());
-        try {
-            latch.await();
-        } catch (InterruptedException ie) {
-            throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
-        }
-
         synchronized (mLock) {
             List<RollbackInfo> rollbacks = new ArrayList<>();
             for (int i = 0; i < mRollbacks.size(); ++i) {
-                RollbackData data = mRollbacks.get(i);
-                if (data.state == RollbackData.ROLLBACK_STATE_AVAILABLE) {
-                    rollbacks.add(data.info);
+                Rollback rollback = mRollbacks.get(i);
+                if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE) {
+                    rollbacks.add(rollback.info);
+                }
+            }
+
+            // Also return new rollbacks for which the PackageRollbackInfo is complete.
+            for (NewRollback newRollback : mNewRollbacks) {
+                if (newRollback.rollback.info.getPackages().size()
+                        == newRollback.packageSessionIds.length
+                        && !newRollback.isCancelled) {
+                    rollbacks.add(newRollback.rollback.info);
                 }
             }
             return new ParceledListSlice<>(rollbacks);
@@ -319,9 +302,9 @@
         synchronized (mLock) {
             List<RollbackInfo> rollbacks = new ArrayList<>();
             for (int i = 0; i < mRollbacks.size(); ++i) {
-                RollbackData data = mRollbacks.get(i);
-                if (data.state == RollbackData.ROLLBACK_STATE_COMMITTED) {
-                    rollbacks.add(data.info);
+                Rollback rollback = mRollbacks.get(i);
+                if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
+                    rollbacks.add(rollback.info);
                 }
             }
             return new ParceledListSlice<>(rollbacks);
@@ -351,11 +334,11 @@
                 final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
 
                 synchronized (mLock) {
-                    Iterator<RollbackData> iter = mRollbacks.iterator();
+                    Iterator<Rollback> iter = mRollbacks.iterator();
                     while (iter.hasNext()) {
-                        RollbackData data = iter.next();
-                        data.timestamp = data.timestamp.plusMillis(timeDifference);
-                        saveRollbackData(data);
+                        Rollback rollback = iter.next();
+                        rollback.timestamp = rollback.timestamp.plusMillis(timeDifference);
+                        saveRollback(rollback);
                     }
                 }
             }
@@ -379,8 +362,8 @@
             String callerPackageName, IntentSender statusReceiver) {
         Slog.i(TAG, "Initiating rollback");
 
-        RollbackData data = getRollbackForId(rollbackId);
-        if (data == null || data.state != RollbackData.ROLLBACK_STATE_AVAILABLE) {
+        Rollback rollback = getRollbackForId(rollbackId);
+        if (rollback == null || rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
             sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
                     "Rollback unavailable");
             return;
@@ -404,14 +387,14 @@
                     PackageInstaller.SessionParams.MODE_FULL_INSTALL);
             parentParams.setRequestDowngrade(true);
             parentParams.setMultiPackage();
-            if (data.isStaged()) {
+            if (rollback.isStaged()) {
                 parentParams.setStaged();
             }
 
             int parentSessionId = packageInstaller.createSession(parentParams);
             PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
 
-            for (PackageRollbackInfo info : data.info.getPackages()) {
+            for (PackageRollbackInfo info : rollback.info.getPackages()) {
                 PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                         PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                 // TODO: We can't get the installerPackageName for apex
@@ -426,7 +409,7 @@
                 params.setRequestDowngrade(true);
                 params.setRequiredInstalledVersionCode(
                         info.getVersionRolledBackFrom().getLongVersionCode());
-                if (data.isStaged()) {
+                if (rollback.isStaged()) {
                     params.setStaged();
                 }
                 if (info.isApex()) {
@@ -435,7 +418,7 @@
                 int sessionId = packageInstaller.createSession(params);
                 PackageInstaller.Session session = packageInstaller.openSession(sessionId);
                 File[] packageCodePaths = RollbackStore.getPackageCodePaths(
-                        data, info.getPackageName());
+                        rollback, info.getPackageName());
                 if (packageCodePaths == null) {
                     sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
                             "Backup copy of package inaccessible");
@@ -476,8 +459,8 @@
                                     // TODO: Could this cause a rollback to be
                                     // resurrected if it should otherwise have
                                     // expired by now?
-                                    data.state = RollbackData.ROLLBACK_STATE_AVAILABLE;
-                                    data.restoreUserDataInProgress = false;
+                                    rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
+                                    rollback.restoreUserDataInProgress = false;
                                 }
                                 sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
                                         "Rollback downgrade install failed: "
@@ -487,17 +470,17 @@
                             }
 
                             synchronized (mLock) {
-                                if (!data.isStaged()) {
+                                if (!rollback.isStaged()) {
                                     // All calls to restoreUserData should have
                                     // completed by now for a non-staged install.
-                                    data.restoreUserDataInProgress = false;
+                                    rollback.restoreUserDataInProgress = false;
                                 }
 
-                                data.info.setCommittedSessionId(parentSessionId);
-                                data.info.getCausePackages().addAll(causePackages);
+                                rollback.info.setCommittedSessionId(parentSessionId);
+                                rollback.info.getCausePackages().addAll(causePackages);
                             }
-                            mRollbackStore.deletePackageCodePaths(data);
-                            saveRollbackData(data);
+                            mRollbackStore.deletePackageCodePaths(rollback);
+                            saveRollback(rollback);
 
                             sendSuccess(statusReceiver);
 
@@ -512,8 +495,8 @@
             );
 
             synchronized (mLock) {
-                data.state = RollbackData.ROLLBACK_STATE_COMMITTED;
-                data.restoreUserDataInProgress = true;
+                rollback.state = Rollback.ROLLBACK_STATE_COMMITTED;
+                rollback.restoreUserDataInProgress = true;
             }
             parentSession.commit(receiver.getIntentSender());
         } catch (IOException e) {
@@ -535,7 +518,7 @@
             updateRollbackLifetimeDurationInMillis();
             synchronized (mLock) {
                 mRollbacks.clear();
-                mRollbacks.addAll(mRollbackStore.loadAllRollbackData());
+                mRollbacks.addAll(mRollbackStore.loadRollbacks());
             }
             latch.countDown();
         });
@@ -553,13 +536,21 @@
                 Manifest.permission.TEST_MANAGE_ROLLBACKS,
                 "expireRollbackForPackage");
         synchronized (mLock) {
-            Iterator<RollbackData> iter = mRollbacks.iterator();
+            Iterator<Rollback> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
-                RollbackData data = iter.next();
-                for (PackageRollbackInfo info : data.info.getPackages()) {
+                Rollback rollback = iter.next();
+                for (PackageRollbackInfo info : rollback.info.getPackages()) {
                     if (info.getPackageName().equals(packageName)) {
                         iter.remove();
-                        deleteRollback(data);
+                        deleteRollback(rollback);
+                        break;
+                    }
+                }
+            }
+            for (NewRollback newRollback : mNewRollbacks) {
+                for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
+                    if (info.getPackageName().equals(packageName)) {
+                        newRollback.isCancelled = true;
                         break;
                     }
                 }
@@ -583,16 +574,16 @@
 
     void onUnlockUser(int userId) {
         getHandler().post(() -> {
-            final List<RollbackData> rollbacks;
+            final List<Rollback> rollbacks;
             synchronized (mLock) {
                 rollbacks = new ArrayList<>(mRollbacks);
             }
 
-            final Set<RollbackData> changed =
+            final Set<Rollback> changed =
                     mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, rollbacks);
 
-            for (RollbackData rd : changed) {
-                saveRollbackData(rd);
+            for (Rollback rollback : changed) {
+                saveRollback(rollback);
             }
         });
     }
@@ -615,19 +606,19 @@
         getHandler().post(() -> {
             // Check to see if any rollback-enabled staged sessions or staged
             // rollback sessions been applied.
-            List<RollbackData> enabling = new ArrayList<>();
-            List<RollbackData> restoreInProgress = new ArrayList<>();
+            List<Rollback> enabling = new ArrayList<>();
+            List<Rollback> restoreInProgress = new ArrayList<>();
             Set<String> apexPackageNames = new HashSet<>();
             synchronized (mLock) {
-                for (RollbackData data : mRollbacks) {
-                    if (data.isStaged()) {
-                        if (data.state == RollbackData.ROLLBACK_STATE_ENABLING) {
-                            enabling.add(data);
-                        } else if (data.restoreUserDataInProgress) {
-                            restoreInProgress.add(data);
+                for (Rollback rollback : mRollbacks) {
+                    if (rollback.isStaged()) {
+                        if (rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
+                            enabling.add(rollback);
+                        } else if (rollback.restoreUserDataInProgress) {
+                            restoreInProgress.add(rollback);
                         }
 
-                        for (PackageRollbackInfo info : data.info.getPackages()) {
+                        for (PackageRollbackInfo info : rollback.info.getPackages()) {
                             if (info.isApex()) {
                                 apexPackageNames.add(info.getPackageName());
                             }
@@ -636,32 +627,32 @@
                 }
             }
 
-            for (RollbackData data : enabling) {
+            for (Rollback rollback : enabling) {
                 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
                 PackageInstaller.SessionInfo session = installer.getSessionInfo(
-                        data.stagedSessionId);
+                        rollback.stagedSessionId);
                 if (session == null || session.isStagedSessionFailed()) {
                     // TODO: Do we need to remove this from
                     // mRollbacks, or is it okay to leave as
                     // unavailable until the next reboot when it will go
                     // away on its own?
-                    deleteRollback(data);
+                    deleteRollback(rollback);
                 } else if (session.isStagedSessionApplied()) {
-                    makeRollbackAvailable(data);
+                    makeRollbackAvailable(rollback);
                 }
             }
 
-            for (RollbackData data : restoreInProgress) {
+            for (Rollback rollback : restoreInProgress) {
                 PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
                 PackageInstaller.SessionInfo session = installer.getSessionInfo(
-                        data.stagedSessionId);
+                        rollback.stagedSessionId);
                 // TODO: What if session is null?
                 if (session != null) {
                     if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
                         synchronized (mLock) {
-                            data.restoreUserDataInProgress = false;
+                            rollback.restoreUserDataInProgress = false;
                         }
-                        saveRollbackData(data);
+                        saveRollback(rollback);
                     }
                 }
             }
@@ -688,19 +679,19 @@
         VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
 
         synchronized (mLock) {
-            Iterator<RollbackData> iter = mRollbacks.iterator();
+            Iterator<Rollback> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
-                RollbackData data = iter.next();
+                Rollback rollback = iter.next();
                 // TODO: Should we remove rollbacks in the ENABLING state here?
-                if (data.state == RollbackData.ROLLBACK_STATE_AVAILABLE
-                        || data.state == RollbackData.ROLLBACK_STATE_ENABLING) {
-                    for (PackageRollbackInfo info : data.info.getPackages()) {
+                if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE
+                        || rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
+                    for (PackageRollbackInfo info : rollback.info.getPackages()) {
                         if (info.getPackageName().equals(packageName)
                                 && !packageVersionsEqual(
                                     info.getVersionRolledBackFrom(),
                                     installedVersion)) {
                             iter.remove();
-                            deleteRollback(data);
+                            deleteRollback(rollback);
                             break;
                         }
                     }
@@ -756,17 +747,18 @@
         Instant now = Instant.now();
         Instant oldest = null;
         synchronized (mLock) {
-            Iterator<RollbackData> iter = mRollbacks.iterator();
+            Iterator<Rollback> iter = mRollbacks.iterator();
             while (iter.hasNext()) {
-                RollbackData data = iter.next();
-                if (data.state != RollbackData.ROLLBACK_STATE_AVAILABLE) {
+                Rollback rollback = iter.next();
+                if (rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
                     continue;
                 }
-                if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
+                if (!now.isBefore(
+                            rollback.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
                     iter.remove();
-                    deleteRollback(data);
-                } else if (oldest == null || oldest.isAfter(data.timestamp)) {
-                    oldest = data.timestamp;
+                    deleteRollback(rollback);
+                } else if (oldest == null || oldest.isAfter(rollback.timestamp)) {
+                    oldest = rollback.timestamp;
                 }
             }
         }
@@ -821,13 +813,12 @@
      *
      * @param installFlags information about what is being installed.
      * @param newPackageCodePath path to the package about to be installed.
-     * @param installedUsers the set of users for which a given package is installed.
      * @param user the user that owns the install session to enable rollback on.
      * @param token the distinct rollback token sent by package manager.
      * @return true if enabling the rollback succeeds, false otherwise.
      */
-    private boolean enableRollback(int installFlags, File newPackageCodePath,
-            int[] installedUsers, @UserIdInt int user, int token) {
+    private boolean enableRollback(
+            int installFlags, File newPackageCodePath, @UserIdInt int user, int token) {
 
         // Find the session id associated with this install.
         // TODO: It would be nice if package manager or package installer told
@@ -872,38 +863,15 @@
 
         // Check to see if this is the apk session for a staged session with
         // rollback enabled.
-        // TODO: This check could be made more efficient.
-        RollbackData rd = null;
         synchronized (mLock) {
             for (int i = 0; i < mRollbacks.size(); ++i) {
-                RollbackData data = mRollbacks.get(i);
-                if (data.apkSessionId == parentSession.getSessionId()) {
-                    rd = data;
-                    break;
-                }
-            }
-        }
-
-        if (rd != null) {
-            // This is the apk session for a staged session. We do not need to create a new rollback
-            // for this session.
-            PackageParser.PackageLite newPackage = null;
-            try {
-                newPackage = PackageParser.parsePackageLite(
-                        new File(packageSession.resolvedBaseCodePath), 0);
-            } catch (PackageParser.PackageParserException e) {
-                Slog.e(TAG, "Unable to parse new package", e);
-                return false;
-            }
-            String packageName = newPackage.packageName;
-            for (PackageRollbackInfo info : rd.info.getPackages()) {
-                if (info.getPackageName().equals(packageName)) {
-                    info.getInstalledUsers().addAll(IntArray.wrap(installedUsers));
+                Rollback rollback = mRollbacks.get(i);
+                if (rollback.apkSessionId == parentSession.getSessionId()) {
+                    // This is the apk session for a staged session with rollback enabled. We do not
+                    // need to create a new rollback for this session.
                     return true;
                 }
             }
-            Slog.e(TAG, "Unable to find package in apk session");
-            return false;
         }
 
         NewRollback newRollback;
@@ -919,7 +887,7 @@
         }
         newRollback.addToken(token);
 
-        return enableRollbackForPackageSession(newRollback.data, packageSession, installedUsers);
+        return enableRollbackForPackageSession(newRollback.rollback, packageSession);
     }
 
     /**
@@ -929,8 +897,8 @@
      *
      * @return true on success, false on failure.
      */
-    private boolean enableRollbackForPackageSession(RollbackData data,
-            PackageInstaller.SessionInfo session, @NonNull int[] installedUsers) {
+    private boolean enableRollbackForPackageSession(Rollback rollback,
+            PackageInstaller.SessionInfo session) {
         // TODO: Don't attempt to enable rollback for split installs.
         final int installFlags = session.installFlags;
         if ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) == 0) {
@@ -988,15 +956,14 @@
         PackageRollbackInfo packageRollbackInfo = new PackageRollbackInfo(
                 newVersion, installedVersion,
                 new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
-                isApex, IntArray.wrap(installedUsers),
-                new SparseLongArray() /* ceSnapshotInodes */);
+                isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
 
         try {
             ApplicationInfo appInfo = pkgInfo.applicationInfo;
-            RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
+            RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
             if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
                 for (String sourceDir : appInfo.splitSourceDirs) {
-                    RollbackStore.backupPackageCodePath(data, packageName, sourceDir);
+                    RollbackStore.backupPackageCodePath(rollback, packageName, sourceDir);
                 }
             }
         } catch (IOException e) {
@@ -1005,7 +972,7 @@
         }
 
         synchronized (mLock) {
-            data.info.getPackages().add(packageRollbackInfo);
+            rollback.info.getPackages().add(packageRollbackInfo);
         }
         return true;
     }
@@ -1019,7 +986,7 @@
         }
 
         getHandler().post(() -> {
-            snapshotUserDataInternal(packageName);
+            snapshotUserDataInternal(packageName, userIds);
             restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token);
             final PackageManagerInternal pmi = LocalServices.getService(
                     PackageManagerInternal.class);
@@ -1027,19 +994,20 @@
         });
     }
 
-    private void snapshotUserDataInternal(String packageName) {
+    private void snapshotUserDataInternal(String packageName, int[] userIds) {
         synchronized (mLock) {
             // staged installs
             for (int i = 0; i < mRollbacks.size(); i++) {
-                RollbackData data = mRollbacks.get(i);
-                if (data.state != RollbackData.ROLLBACK_STATE_ENABLING) {
+                Rollback rollback = mRollbacks.get(i);
+                if (rollback.state != Rollback.ROLLBACK_STATE_ENABLING) {
                     continue;
                 }
 
-                for (PackageRollbackInfo info : data.info.getPackages()) {
+                for (PackageRollbackInfo info : rollback.info.getPackages()) {
                     if (info.getPackageName().equals(packageName)) {
-                        mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info);
-                        saveRollbackData(data);
+                        mAppDataRollbackHelper.snapshotAppData(
+                                rollback.info.getRollbackId(), info, userIds);
+                        saveRollback(rollback);
                         break;
                     }
                 }
@@ -1047,11 +1015,11 @@
             // non-staged installs
             PackageRollbackInfo info;
             for (NewRollback rollback : mNewRollbacks) {
-                info = getPackageRollbackInfo(rollback.data, packageName);
+                info = getPackageRollbackInfo(rollback.rollback, packageName);
                 if (info != null) {
-                    mAppDataRollbackHelper.snapshotAppData(rollback.data.info.getRollbackId(),
-                            info);
-                    saveRollbackData(rollback.data);
+                    mAppDataRollbackHelper.snapshotAppData(
+                            rollback.rollback.info.getRollbackId(), info, userIds);
+                    saveRollback(rollback.rollback);
                 }
             }
         }
@@ -1060,31 +1028,31 @@
     private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
             long ceDataInode, String seInfo, int token) {
         PackageRollbackInfo info = null;
-        RollbackData rollbackData = null;
+        Rollback rollback = null;
         synchronized (mLock) {
             for (int i = 0; i < mRollbacks.size(); ++i) {
-                RollbackData data = mRollbacks.get(i);
-                if (data.restoreUserDataInProgress) {
-                    info = getPackageRollbackInfo(data, packageName);
+                Rollback candidate = mRollbacks.get(i);
+                if (candidate.restoreUserDataInProgress) {
+                    info = getPackageRollbackInfo(candidate, packageName);
                     if (info != null) {
-                        rollbackData = data;
+                        rollback = candidate;
                         break;
                     }
                 }
             }
         }
 
-        if (rollbackData == null) {
+        if (rollback == null) {
             return;
         }
 
         for (int userId : userIds) {
-            final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
-                    rollbackData.info.getRollbackId(), info, userId, appId, seInfo);
+            final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
+                    rollback.info.getRollbackId(), info, userId, appId, seInfo);
 
             // We've updated metadata about this rollback, so save it to flash.
-            if (changedRollbackData) {
-                saveRollbackData(rollbackData);
+            if (changedRollback) {
+                saveRollback(rollback);
             }
         }
     }
@@ -1114,8 +1082,7 @@
             }
 
             if (!session.isMultiPackage()) {
-                if (!enableRollbackForPackageSession(newRollback.data, session,
-                            new int[0])) {
+                if (!enableRollbackForPackageSession(newRollback.rollback, session)) {
                     Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
                     result.offer(false);
                     return;
@@ -1129,8 +1096,7 @@
                         result.offer(false);
                         return;
                     }
-                    if (!enableRollbackForPackageSession(newRollback.data, childSession,
-                                new int[0])) {
+                    if (!enableRollbackForPackageSession(newRollback.rollback, childSession)) {
                         Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
                         result.offer(false);
                         return;
@@ -1155,20 +1121,20 @@
             throw new SecurityException("notifyStagedApkSession may only be called by the system.");
         }
         getHandler().post(() -> {
-            RollbackData rd = null;
+            Rollback rollback = null;
             synchronized (mLock) {
                 for (int i = 0; i < mRollbacks.size(); ++i) {
-                    RollbackData data = mRollbacks.get(i);
-                    if (data.stagedSessionId == originalSessionId) {
-                        data.apkSessionId = apkSessionId;
-                        rd = data;
+                    Rollback candidate = mRollbacks.get(i);
+                    if (candidate.stagedSessionId == originalSessionId) {
+                        candidate.apkSessionId = apkSessionId;
+                        rollback = candidate;
                         break;
                     }
                 }
             }
 
-            if (rd != null) {
-                saveRollbackData(rd);
+            if (rollback != null) {
+                saveRollback(rollback);
             }
         });
     }
@@ -1278,7 +1244,7 @@
             }
 
             if (newRollback != null) {
-                RollbackData rollback = completeEnableRollback(newRollback, success);
+                Rollback rollback = completeEnableRollback(newRollback, success);
                 if (rollback != null && !rollback.isStaged()) {
                     makeRollbackAvailable(rollback);
                 }
@@ -1291,32 +1257,32 @@
      * This should be called after rollback has been enabled for all packages
      * in the rollback. It does not make the rollback available yet.
      *
-     * @return the rollback data for a successfully enable-completed rollback,
+     * @return the Rollback instance for a successfully enable-completed rollback,
      * or null on error.
      */
-    private RollbackData completeEnableRollback(NewRollback newRollback, boolean success) {
-        RollbackData data = newRollback.data;
+    private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
+        Rollback rollback = newRollback.rollback;
         if (!success) {
             // The install session was aborted, clean up the pending install.
-            deleteRollback(data);
+            deleteRollback(rollback);
             return null;
         }
         if (newRollback.isCancelled) {
             Slog.e(TAG, "Rollback has been cancelled by PackageManager");
-            deleteRollback(data);
+            deleteRollback(rollback);
             return null;
         }
 
-        // It's safe to access data.info outside a synchronized block because
+        // It's safe to access rollback.info outside a synchronized block because
         // this is running on the handler thread and all changes to the
-        // data.info occur on the handler thread.
-        if (data.info.getPackages().size() != newRollback.packageSessionIds.length) {
+        // rollback.info occur on the handler thread.
+        if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
             Slog.e(TAG, "Failed to enable rollback for all packages in session.");
-            deleteRollback(data);
+            deleteRollback(rollback);
             return null;
         }
 
-        saveRollbackData(data);
+        saveRollback(rollback);
         synchronized (mLock) {
             // Note: There is a small window of time between when
             // the session has been committed by the package
@@ -1324,25 +1290,25 @@
             // here. Presumably the window is small enough that
             // nobody will want to roll back the newly installed
             // package before we make the rollback available.
-            // TODO: We'll lose the rollback data if the
+            // TODO: We'll lose the rollback if the
             // device reboots between when the session is
             // committed and this point. Revisit this after
             // adding support for rollback of staged installs.
-            mRollbacks.add(data);
+            mRollbacks.add(rollback);
         }
 
-        return data;
+        return rollback;
     }
 
-    private void makeRollbackAvailable(RollbackData data) {
+    private void makeRollbackAvailable(Rollback rollback) {
         // TODO: What if the rollback has since been expired, for example due
         // to a new package being installed. Won't this revive an expired
         // rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this.
         synchronized (mLock) {
-            data.state = RollbackData.ROLLBACK_STATE_AVAILABLE;
-            data.timestamp = Instant.now();
+            rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
+            rollback.timestamp = Instant.now();
         }
-        saveRollbackData(data);
+        saveRollback(rollback);
 
         // TODO(zezeozue): Provide API to explicitly start observing instead
         // of doing this for all rollbacks. If we do this for all rollbacks,
@@ -1350,8 +1316,8 @@
         // After enabling and commiting any rollback, observe packages and
         // prepare to rollback if packages crashes too frequently.
         List<String> packages = new ArrayList<>();
-        for (int i = 0; i < data.info.getPackages().size(); i++) {
-            packages.add(data.info.getPackages().get(i).getPackageName());
+        for (int i = 0; i < rollback.info.getPackages().size(); i++) {
+            packages.add(rollback.info.getPackages().get(i).getPackageName());
         }
         mPackageHealthObserver.startObservingHealth(packages,
                 mRollbackLifetimeDurationInMillis);
@@ -1359,15 +1325,14 @@
     }
 
     /*
-     * Returns the RollbackData, if any, for a rollback with the given
-     * rollbackId.
+     * Returns the rollback with the given rollbackId, if any.
      */
-    private RollbackData getRollbackForId(int rollbackId) {
+    private Rollback getRollbackForId(int rollbackId) {
         synchronized (mLock) {
             for (int i = 0; i < mRollbacks.size(); ++i) {
-                RollbackData data = mRollbacks.get(i);
-                if (data.info.getRollbackId() == rollbackId) {
-                    return data;
+                Rollback rollback = mRollbacks.get(i);
+                if (rollback.info.getRollbackId() == rollbackId) {
+                    return rollback;
                 }
             }
         }
@@ -1377,11 +1342,11 @@
 
     /**
      * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
-     * a specified {@code RollbackData}.
+     * a specified {@code Rollback}.
      */
-    private static PackageRollbackInfo getPackageRollbackInfo(RollbackData data,
+    private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
             String packageName) {
-        for (PackageRollbackInfo info : data.info.getPackages()) {
+        for (PackageRollbackInfo info : rollback.info.getPackages()) {
             if (info.getPackageName().equals(packageName)) {
                 return info;
             }
@@ -1405,30 +1370,30 @@
         throw new IllegalStateException("Failed to allocate rollback ID");
     }
 
-    private void deleteRollback(RollbackData rollbackData) {
-        for (PackageRollbackInfo info : rollbackData.info.getPackages()) {
-            IntArray installedUsers = info.getInstalledUsers();
-            for (int i = 0; i < installedUsers.size(); i++) {
-                int userId = installedUsers.get(i);
-                mAppDataRollbackHelper.destroyAppDataSnapshot(rollbackData.info.getRollbackId(),
+    private void deleteRollback(Rollback rollback) {
+        for (PackageRollbackInfo info : rollback.info.getPackages()) {
+            IntArray snapshottedUsers = info.getSnapshottedUsers();
+            for (int i = 0; i < snapshottedUsers.size(); i++) {
+                int userId = snapshottedUsers.get(i);
+                mAppDataRollbackHelper.destroyAppDataSnapshot(rollback.info.getRollbackId(),
                         info, userId);
             }
         }
-        mRollbackStore.deleteRollbackData(rollbackData);
+        mRollbackStore.deleteRollback(rollback);
     }
 
     /**
-     * Saves rollback data, swallowing any IOExceptions.
+     * Saves a rollback, swallowing any IOExceptions.
      * For those times when it's not obvious what to do about the IOException.
      * TODO: Double check we can't do a better job handling the IOException in
      * a cases where this method is called.
      */
-    private void saveRollbackData(RollbackData rollbackData) {
+    private void saveRollback(Rollback rollback) {
         try {
-            mRollbackStore.saveRollbackData(rollbackData);
+            mRollbackStore.saveRollback(rollback);
         } catch (IOException ioe) {
-            Slog.e(TAG, "Unable to save rollback info for: "
-                    + rollbackData.info.getRollbackId(), ioe);
+            Slog.e(TAG, "Unable to save rollback for: "
+                    + rollback.info.getRollbackId(), ioe);
         }
     }
 
@@ -1436,14 +1401,14 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
         synchronized (mLock) {
-            for (RollbackData data : mRollbacks) {
-                RollbackInfo info = data.info;
+            for (Rollback rollback : mRollbacks) {
+                RollbackInfo info = rollback.info;
                 ipw.println(info.getRollbackId() + ":");
                 ipw.increaseIndent();
-                ipw.println("-state: " + data.getStateAsString());
-                ipw.println("-timestamp: " + data.timestamp);
-                if (data.stagedSessionId != -1) {
-                    ipw.println("-stagedSessionId: " + data.stagedSessionId);
+                ipw.println("-state: " + rollback.getStateAsString());
+                ipw.println("-timestamp: " + rollback.timestamp);
+                if (rollback.stagedSessionId != -1) {
+                    ipw.println("-stagedSessionId: " + rollback.stagedSessionId);
                 }
                 ipw.println("-packages:");
                 ipw.increaseIndent();
@@ -1453,7 +1418,7 @@
                             + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
                 }
                 ipw.decreaseIndent();
-                if (data.state == RollbackData.ROLLBACK_STATE_COMMITTED) {
+                if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
                     ipw.println("-causePackages:");
                     ipw.increaseIndent();
                     for (VersionedPackage cPkg : info.getCausePackages()) {
@@ -1479,7 +1444,7 @@
     }
 
     private static class NewRollback {
-        public final RollbackData data;
+        public final Rollback rollback;
 
         /**
          * This array holds all of the rollback tokens associated with package sessions included
@@ -1497,9 +1462,9 @@
         public final int[] packageSessionIds;
 
         /**
-         * Flag to determine whether the RollbackData has been cancelled.
+         * Flag to determine whether the rollback has been cancelled.
          *
-         * <p>RollbackData could be invalidated and cancelled if RollbackManager receives
+         * <p>Rollback could be invalidated and cancelled if RollbackManager receives
          * {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
          *
          * <p>The main underlying assumption here is that if enabling the rollback times out, then
@@ -1509,8 +1474,8 @@
          */
         public boolean isCancelled = false;
 
-        NewRollback(RollbackData data, int[] packageSessionIds) {
-            this.data = data;
+        NewRollback(Rollback rollback, int[] packageSessionIds) {
+            this.rollback = rollback;
             this.packageSessionIds = packageSessionIds;
         }
 
@@ -1525,13 +1490,13 @@
 
     NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
         int rollbackId = allocateRollbackIdLocked();
-        final RollbackData data;
+        final Rollback rollback;
         int parentSessionId = parentSession.getSessionId();
 
         if (parentSession.isStaged()) {
-            data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
+            rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
         } else {
-            data = mRollbackStore.createNonStagedRollback(rollbackId);
+            rollback = mRollbackStore.createNonStagedRollback(rollbackId);
         }
 
         int[] packageSessionIds;
@@ -1541,7 +1506,7 @@
             packageSessionIds = new int[]{parentSessionId};
         }
 
-        return new NewRollback(data, packageSessionIds);
+        return new NewRollback(rollback, packageSessionIds);
     }
 
     /**
@@ -1552,10 +1517,10 @@
     NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
         // We expect mNewRollbacks to be a very small list; linear search
         // should be plenty fast.
-        for (NewRollback newRollbackData : mNewRollbacks) {
-            for (int id : newRollbackData.packageSessionIds) {
+        for (NewRollback newRollback: mNewRollbacks) {
+            for (int id : newRollback.packageSessionIds) {
                 if (id == packageSessionId) {
-                    return newRollbackData;
+                    return newRollback;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 1c36dc7..b2448f6 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -16,8 +16,8 @@
 
 package com.android.server.rollback;
 
-import static com.android.server.rollback.RollbackData.rollbackStateFromString;
-import static com.android.server.rollback.RollbackData.rollbackStateToString;
+import static com.android.server.rollback.Rollback.rollbackStateFromString;
+import static com.android.server.rollback.Rollback.rollbackStateToString;
 
 import android.annotation.NonNull;
 import android.content.pm.VersionedPackage;
@@ -73,17 +73,17 @@
     }
 
     /**
-     * Reads the rollback data from persistent storage.
+     * Reads the rollbacks from persistent storage.
      */
-    List<RollbackData> loadAllRollbackData() {
-        List<RollbackData> rollbacks = new ArrayList<>();
+    List<Rollback> loadRollbacks() {
+        List<Rollback> rollbacks = new ArrayList<>();
         mRollbackDataDir.mkdirs();
         for (File rollbackDir : mRollbackDataDir.listFiles()) {
             if (rollbackDir.isDirectory()) {
                 try {
-                    rollbacks.add(loadRollbackData(rollbackDir));
+                    rollbacks.add(loadRollback(rollbackDir));
                 } catch (IOException e) {
-                    Slog.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+                    Slog.e(TAG, "Unable to read rollback at " + rollbackDir, e);
                     removeFile(rollbackDir);
                 }
             }
@@ -191,21 +191,21 @@
     }
 
     /**
-     * Creates a new RollbackData instance for a non-staged rollback with
+     * Creates a new Rollback instance for a non-staged rollback with
      * backupDir assigned.
      */
-    RollbackData createNonStagedRollback(int rollbackId) {
+    Rollback createNonStagedRollback(int rollbackId) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
-        return new RollbackData(rollbackId, backupDir, -1);
+        return new Rollback(rollbackId, backupDir, -1);
     }
 
     /**
-     * Creates a new RollbackData instance for a staged rollback with
+     * Creates a new Rollback instance for a staged rollback with
      * backupDir assigned.
      */
-    RollbackData createStagedRollback(int rollbackId, int stagedSessionId) {
+    Rollback createStagedRollback(int rollbackId, int stagedSessionId) {
         File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
-        return new RollbackData(rollbackId, backupDir, stagedSessionId);
+        return new Rollback(rollbackId, backupDir, stagedSessionId);
     }
 
     /**
@@ -213,10 +213,10 @@
      * For packages containing splits, this method should be called for each
      * of the package's split apks in addition to the base apk.
      */
-    static void backupPackageCodePath(RollbackData data, String packageName, String codePath)
+    static void backupPackageCodePath(Rollback rollback, String packageName, String codePath)
             throws IOException {
         File sourceFile = new File(codePath);
-        File targetDir = new File(data.backupDir, packageName);
+        File targetDir = new File(rollback.backupDir, packageName);
         targetDir.mkdirs();
         File targetFile = new File(targetDir, sourceFile.getName());
 
@@ -228,8 +228,8 @@
      * Returns the apk or apex files backed up for the given package.
      * Includes the base apk and any splits. Returns null if none found.
      */
-    static File[] getPackageCodePaths(RollbackData data, String packageName) {
-        File targetDir = new File(data.backupDir, packageName);
+    static File[] getPackageCodePaths(Rollback rollback, String packageName) {
+        File targetDir = new File(rollback.backupDir, packageName);
         File[] files = targetDir.listFiles();
         if (files == null || files.length == 0) {
             return null;
@@ -241,27 +241,27 @@
      * Deletes all backed up apks and apex files associated with the given
      * rollback.
      */
-    static void deletePackageCodePaths(RollbackData data) {
-        for (PackageRollbackInfo info : data.info.getPackages()) {
-            File targetDir = new File(data.backupDir, info.getPackageName());
+    static void deletePackageCodePaths(Rollback rollback) {
+        for (PackageRollbackInfo info : rollback.info.getPackages()) {
+            File targetDir = new File(rollback.backupDir, info.getPackageName());
             removeFile(targetDir);
         }
     }
 
     /**
-     * Saves the rollback data to persistent storage.
+     * Saves the given rollback to persistent storage.
      */
-    void saveRollbackData(RollbackData data) throws IOException {
+    void saveRollback(Rollback rollback) throws IOException {
         try {
             JSONObject dataJson = new JSONObject();
-            dataJson.put("info", rollbackInfoToJson(data.info));
-            dataJson.put("timestamp", data.timestamp.toString());
-            dataJson.put("stagedSessionId", data.stagedSessionId);
-            dataJson.put("state", rollbackStateToString(data.state));
-            dataJson.put("apkSessionId", data.apkSessionId);
-            dataJson.put("restoreUserDataInProgress", data.restoreUserDataInProgress);
+            dataJson.put("info", rollbackInfoToJson(rollback.info));
+            dataJson.put("timestamp", rollback.timestamp.toString());
+            dataJson.put("stagedSessionId", rollback.stagedSessionId);
+            dataJson.put("state", rollbackStateToString(rollback.state));
+            dataJson.put("apkSessionId", rollback.apkSessionId);
+            dataJson.put("restoreUserDataInProgress", rollback.restoreUserDataInProgress);
 
-            PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json"));
+            PrintWriter pw = new PrintWriter(new File(rollback.backupDir, "rollback.json"));
             pw.println(dataJson.toString());
             pw.close();
         } catch (JSONException e) {
@@ -270,23 +270,23 @@
     }
 
     /**
-     * Removes all persistant storage associated with the given rollback data.
+     * Removes all persistent storage associated with the given rollback.
      */
-    void deleteRollbackData(RollbackData data) {
-        removeFile(data.backupDir);
+    void deleteRollback(Rollback rollback) {
+        removeFile(rollback.backupDir);
     }
 
     /**
      * Reads the metadata for a rollback from the given directory.
      * @throws IOException in case of error reading the data.
      */
-    private static RollbackData loadRollbackData(File backupDir) throws IOException {
+    private static Rollback loadRollback(File backupDir) throws IOException {
         try {
             File rollbackJsonFile = new File(backupDir, "rollback.json");
             JSONObject dataJson = new JSONObject(
                     IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));
 
-            return new RollbackData(
+            return new Rollback(
                     rollbackInfoFromJson(dataJson.getJSONObject("info")),
                     backupDir,
                     Instant.parse(dataJson.getString("timestamp")),
@@ -319,13 +319,14 @@
 
         IntArray pendingBackups = info.getPendingBackups();
         List<RestoreInfo> pendingRestores = info.getPendingRestores();
-        IntArray installedUsers = info.getInstalledUsers();
+        IntArray snapshottedUsers = info.getSnapshottedUsers();
         json.put("pendingBackups", convertToJsonArray(pendingBackups));
         json.put("pendingRestores", convertToJsonArray(pendingRestores));
 
         json.put("isApex", info.isApex());
 
-        json.put("installedUsers", convertToJsonArray(installedUsers));
+        // Field is named 'installedUsers' for legacy reasons.
+        json.put("installedUsers", convertToJsonArray(snapshottedUsers));
         json.put("ceSnapshotInodes", ceSnapshotInodesToJson(info.getCeSnapshotInodes()));
 
         return json;
@@ -345,12 +346,13 @@
 
         final boolean isApex = json.getBoolean("isApex");
 
-        final IntArray installedUsers = convertToIntArray(json.getJSONArray("installedUsers"));
+        // Field is named 'installedUsers' for legacy reasons.
+        final IntArray snapshottedUsers = convertToIntArray(json.getJSONArray("installedUsers"));
         final SparseLongArray ceSnapshotInodes = ceSnapshotInodesFromJson(
                 json.getJSONArray("ceSnapshotInodes"));
 
         return new PackageRollbackInfo(versionRolledBackFrom, versionRolledBackTo,
-                pendingBackups, pendingRestores, isApex, installedUsers, ceSnapshotInodes);
+                pendingBackups, pendingRestores, isApex, snapshottedUsers, ceSnapshotInodes);
     }
 
     private static JSONArray versionedPackagesToJson(List<VersionedPackage> packages)
diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java
index 2f681a3..3c06d0e 100644
--- a/services/core/java/com/android/server/security/KeyChainSystemService.java
+++ b/services/core/java/com/android/server/security/KeyChainSystemService.java
@@ -27,7 +27,7 @@
 import android.security.IKeyChainService;
 import android.util.Slog;
 
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
@@ -99,8 +99,8 @@
         }
 
         final String packageName = intent.getComponent().getPackageName();
-        final DeviceIdleController.LocalService idleController =
-                LocalServices.getService(DeviceIdleController.LocalService.class);
+        final DeviceIdleInternal idleController =
+                LocalServices.getService(DeviceIdleInternal.class);
         idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
                 KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, "keychain");
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d67048f..8897eca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -610,11 +610,12 @@
 
     @Override
     public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
-            int type, boolean requireConfirmation, int userId) {
+            int type, boolean requireConfirmation, int userId, String opPackageName) {
         enforceBiometricDialog();
         if (mBar != null) {
             try {
-                mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId);
+                mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId,
+                        opPackageName);
             } catch (RemoteException ex) {
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index f1cd721..5a0dfd0 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -41,6 +41,7 @@
 import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.am.ActivityDisplayProto.STACKS;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
@@ -173,18 +174,10 @@
         mService = root.mService;
         mDisplayId = display.getDisplayId();
         mDisplay = display;
-        mDisplayContent = createDisplayContent();
+        mDisplayContent = mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
         mDisplayContent.reconfigureDisplayLocked();
-        updateBounds();
-    }
-
-    protected DisplayContent createDisplayContent() {
-        return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
-    }
-
-    private void updateBounds() {
-        mDisplay.getRealSize(mTmpDisplaySize);
-        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+        onRequestedOverrideConfigurationChanged(
+                mDisplayContent.getRequestedOverrideConfiguration());
     }
 
     void onDisplayChanged() {
@@ -200,7 +193,8 @@
             }
         }
 
-        updateBounds();
+        mDisplay.getRealSize(mTmpDisplaySize);
+        setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
         if (mDisplayContent != null) {
             mDisplayContent.updateDisplayInfo();
             mService.mWindowManager.requestTraversal();
@@ -1541,6 +1535,17 @@
         return mSingleTaskInstance;
     }
 
+    @VisibleForTesting
+    void removeAllTasks() {
+        for (int i = getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getChildAt(i);
+            final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+            for (int j = tasks.size() - 1; j >= 0; --j) {
+                stack.removeTask(tasks.get(j), "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+            }
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
                 + (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 31e8bbdab..2269537 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,6 +121,7 @@
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
 import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
@@ -1372,16 +1373,17 @@
         return stack != null ? stack.getDisplay() : null;
     }
 
-    boolean changeWindowTranslucency(boolean toOpaque) {
-        if (fullscreen == toOpaque) {
-            return false;
+    boolean setOccludesParent(boolean occludesParent) {
+        final boolean changed = mAppWindowToken.setOccludesParent(occludesParent);
+        if (changed) {
+            if (!occludesParent) {
+                getActivityStack().convertActivityToTranslucent(this);
+            }
+            // Keep track of the number of fullscreen activities in this task.
+            task.numFullscreen += occludesParent ? +1 : -1;
+            mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
         }
-
-        // Keep track of the number of fullscreen activities in this task.
-        task.numFullscreen += toOpaque ? +1 : -1;
-
-        fullscreen = toOpaque;
-        return true;
+        return changed;
     }
 
     void takeFromHistory() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 17536e4..daf3286 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2492,7 +2492,7 @@
             mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
 
             if (waitingActivity != null) {
-                mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
+                mWindowManager.setWindowOpaqueLocked(waitingActivity.appToken, false);
                 if (waitingActivity.attachedToProcess()) {
                     try {
                         waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
@@ -2813,7 +2813,7 @@
         // Launching this app's activity, make sure the app is no longer
         // considered stopped.
         try {
-            AppGlobals.getPackageManager().setPackageStoppedState(
+            mService.getPackageManager().setPackageStoppedState(
                     next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
         } catch (RemoteException e1) {
         } catch (IllegalArgumentException e) {
@@ -5370,13 +5370,20 @@
     void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
             boolean fromFullscreen) {
         if (!inPinnedWindowingMode()) return;
-        if (skipResizeAnimation(toBounds == null /* toFullscreen */)) {
-            mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
-        } else {
-            if (getTaskStack() == null) return;
-            getTaskStack().animateResizePinnedStack(toBounds, sourceHintBounds,
-                    animationDuration, fromFullscreen);
+        if (toBounds == null /* toFullscreen */) {
+            final Configuration parentConfig = getParent().getConfiguration();
+            final ActivityRecord top = topRunningNonOverlayTaskActivity();
+            if (top != null && !top.isConfigurationCompatible(parentConfig)) {
+                // The final orientation of this activity will change after moving to full screen.
+                // Start freezing screen here to prevent showing a temporary full screen window.
+                top.startFreezingScreenLocked(top.app, CONFIG_SCREEN_LAYOUT);
+                mService.moveTasksToFullscreenStack(mStackId, true /* onTop */);
+                return;
+            }
         }
+        if (getTaskStack() == null) return;
+        getTaskStack().animateResizePinnedStack(toBounds, sourceHintBounds,
+                animationDuration, fromFullscreen);
     }
 
     /**
@@ -5392,15 +5399,6 @@
         stack.getAnimationOrCurrentBounds(outBounds);
     }
 
-    private boolean skipResizeAnimation(boolean toFullscreen) {
-        if (!toFullscreen) {
-            return false;
-        }
-        final Configuration parentConfig = getParent().getConfiguration();
-        final ActivityRecord top = topRunningNonOverlayTaskActivity();
-        return top != null && !top.isConfigurationCompatible(parentConfig);
-    }
-
     void setPictureInPictureAspectRatio(float aspectRatio) {
         if (getTaskStack() == null) return;
         getTaskStack().setPictureInPictureAspectRatio(aspectRatio);
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1c56a10..22f72a4 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -250,7 +250,7 @@
     RecentTasks mRecentTasks;
 
     /** Helper class to abstract out logic for fetching the set of currently running tasks */
-    RunningTasks mRunningTasks;
+    private RunningTasks mRunningTasks;
 
     final ActivityStackSupervisorHandler mHandler;
     final Looper mLooper;
@@ -444,7 +444,7 @@
         }
 
         mInitialized = true;
-        mRunningTasks = createRunningTasks();
+        setRunningTasks(new RunningTasks());
 
         mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
                 mHandler.getLooper());
@@ -485,13 +485,20 @@
     }
 
     void setRecentTasks(RecentTasks recentTasks) {
+        if (mRecentTasks != null) {
+            mRecentTasks.unregisterCallback(this);
+        }
         mRecentTasks = recentTasks;
         mRecentTasks.registerCallback(this);
     }
 
     @VisibleForTesting
-    RunningTasks createRunningTasks() {
-        return new RunningTasks();
+    void setRunningTasks(RunningTasks runningTasks) {
+        mRunningTasks = runningTasks;
+    }
+
+    RunningTasks getRunningTasks() {
+        return mRunningTasks;
     }
 
     /**
@@ -2735,7 +2742,7 @@
         mWindowManager.deferSurfaceLayout();
         try {
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                mWindowManager.setDockedStackCreateState(
+                mWindowManager.setDockedStackCreateStateLocked(
                         activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
 
                 // Defer updating the stack in which recents is until the app transition is done, to
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a3ab27e..7283ca5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -43,7 +43,6 @@
 import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
@@ -361,7 +360,7 @@
     /* Global service lock used by the package the owns this service. */
     final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
     /**
-     * It is the same instance as {@link mGlobalLock}, just declared as a type that the
+     * It is the same instance as {@link #mGlobalLock}, just declared as a type that the
      * locked-region-code-injection does't recognize it. It is used to skip wrapping priority
      * booster for places that are already in the scope of another booster (e.g. computing oom-adj).
      *
@@ -730,7 +729,6 @@
         final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
         final boolean forceResizable = Settings.Global.getInt(
                 resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
-        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
 
         // Transfer any global setting for forcing RTL layout, into a System Property
         DisplayProperties.debug_force_rtl(forceRtl);
@@ -761,10 +759,6 @@
                 mSupportsPictureInPicture = false;
                 mSupportsMultiDisplay = false;
             }
-            mWindowManager.setForceResizableTasks(mForceResizableActivities);
-            mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
-            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
-            mWindowManager.setIsPc(isPc);
             mWindowManager.mRoot.onSettingsRetrieved();
             // This happens before any activities are started, so we can change global configuration
             // in-place.
@@ -821,8 +815,7 @@
                 new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
         mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
         mActivityStartController = new ActivityStartController(this);
-        mRecentTasks = createRecentTasks();
-        mStackSupervisor.setRecentTasks(mRecentTasks);
+        setRecentTasks(new RecentTasks(this, mStackSupervisor));
         mVrController = new VrController(mGlobalLock);
         mKeyguardController = mStackSupervisor.getKeyguardController();
     }
@@ -890,8 +883,10 @@
         return mode == AppOpsManager.MODE_ALLOWED;
     }
 
-    protected RecentTasks createRecentTasks() {
-        return new RecentTasks(this, mStackSupervisor);
+    @VisibleForTesting
+    protected void setRecentTasks(RecentTasks recentTasks) {
+        mRecentTasks = recentTasks;
+        mStackSupervisor.setRecentTasks(recentTasks);
     }
 
     RecentTasks getRecentTasks() {
@@ -1954,12 +1949,7 @@
                 if (r == null) {
                     return false;
                 }
-                final boolean translucentChanged = r.changeWindowTranslucency(true);
-                if (translucentChanged) {
-                    mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-                }
-                mWindowManager.setAppFullscreen(token, true);
-                return translucentChanged;
+                return r.setOccludesParent(true);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -1982,13 +1972,7 @@
                     ActivityRecord under = task.mActivities.get(index - 1);
                     under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
                 }
-                final boolean translucentChanged = r.changeWindowTranslucency(false);
-                if (translucentChanged) {
-                    r.getActivityStack().convertActivityToTranslucent(r);
-                }
-                mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
-                mWindowManager.setAppFullscreen(token, false);
-                return translucentChanged;
+                return r.setOccludesParent(false);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -2581,7 +2565,7 @@
                             + taskId + " to stack " + stackId);
                 }
                 if (stack.inSplitScreenPrimaryWindowingMode()) {
-                    mWindowManager.setDockedStackCreateState(
+                    mWindowManager.setDockedStackCreateStateLocked(
                             SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
                 }
                 task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
@@ -2700,7 +2684,7 @@
                             + " non-standard task " + taskId + " to split-screen windowing mode");
                 }
 
-                mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+                mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
                 final int windowingMode = task.getWindowingMode();
                 final ActivityStack stack = task.getStack();
                 if (toTop) {
@@ -2802,7 +2786,7 @@
         try {
             synchronized (mGlobalLock) {
                 // Cancel the recents animation synchronously (do not hold the WM lock)
-                mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+                mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
                         ? REORDER_MOVE_TO_ORIGINAL_POSITION
                         : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
             }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7fde6de..7e0d9a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -92,6 +92,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -153,8 +154,11 @@
     final ComponentName mActivityComponent;
     final boolean mVoiceInteraction;
 
-    /** @see WindowContainer#fillsParent() */
-    private boolean mFillsParent;
+    /**
+     * The activity is opaque and fills the entire space of this task.
+     * @see WindowContainer#fillsParent()
+     */
+    private boolean mOccludesParent;
     boolean mShowForAllUsers;
     int mTargetSdk;
 
@@ -373,7 +377,7 @@
         appToken = token;
         mActivityComponent = activityComponent;
         mVoiceInteraction = voiceInteraction;
-        mFillsParent = fillsParent;
+        mOccludesParent = fillsParent;
         mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
     }
 
@@ -2354,11 +2358,29 @@
 
     @Override
     boolean fillsParent() {
-        return mFillsParent;
+        return occludesParent();
     }
 
-    void setFillsParent(boolean fillsParent) {
-        mFillsParent = fillsParent;
+    /** Returns true if this activity is opaque and fills the entire space of this task. */
+    boolean occludesParent() {
+        return mOccludesParent;
+    }
+
+    boolean setOccludesParent(boolean occludesParent) {
+        final boolean changed = occludesParent != mOccludesParent;
+        mOccludesParent = occludesParent;
+        setMainWindowOpaque(occludesParent);
+        mWmService.mWindowPlacerLocked.requestTraversal();
+        return changed;
+    }
+
+    void setMainWindowOpaque(boolean isOpaque) {
+        final WindowState win = findMainWindow();
+        if (win == null) {
+            return;
+        }
+        isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
+        win.mWinAnimator.setOpaqueLocked(isOpaque);
     }
 
     boolean containsDismissKeyguardWindow() {
@@ -3035,7 +3057,7 @@
         }
         pw.println(prefix + "component=" + mActivityComponent.flattenToShortString());
         pw.print(prefix); pw.print("task="); pw.println(getTask());
-        pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
+        pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
                 pw.print(" mOrientation="); pw.println(mOrientation);
         pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
             + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -3152,7 +3174,7 @@
         if (mThumbnail != null){
             mThumbnail.writeToProto(proto, THUMBNAIL);
         }
-        proto.write(FILLS_PARENT, mFillsParent);
+        proto.write(FILLS_PARENT, mOccludesParent);
         proto.write(APP_STOPPED, mAppStopped);
         proto.write(HIDDEN_REQUESTED, hiddenRequested);
         proto.write(CLIENT_HIDDEN, mClientHidden);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 282ed42..410cc94 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -295,7 +295,8 @@
                 false /* forceRelayout */);
     }
 
-    private void setUserRotation(int userRotationMode, int userRotation) {
+    @VisibleForTesting
+    void setUserRotation(int userRotationMode, int userRotation) {
         if (isDefaultDisplay) {
             // We'll be notified via settings listener, so we don't need to update internal values.
             final ContentResolver res = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 207e8ef..8507918 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -275,14 +275,14 @@
         // This display used to be in freeform, but we don't support freeform anymore, so fall
         // back to fullscreen.
         if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
-                && !mService.mSupportsFreeformWindowManagement) {
+                && !mService.mAtmService.mSupportsFreeformWindowManagement) {
             return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
         }
         // No record is present so use default windowing mode policy.
         if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
             final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
                     && displayId != Display.DEFAULT_DISPLAY;
-            windowingMode = mService.mSupportsFreeformWindowManagement
+            windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
                     && (mService.mIsPc || forceDesktopMode)
                     ? WindowConfiguration.WINDOWING_MODE_FREEFORM
                     : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b1bc2197..120ce3e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -355,7 +355,9 @@
 
     void getTouchRegion(Rect outRegion) {
         outRegion.set(mTouchRegion);
-        outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+        if (mWindow != null) {
+            outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+        }
     }
 
     private void resetDragResizingChangeReported() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 4f0332c..caa8363 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -241,7 +241,7 @@
             // Fetch all the surface controls and pass them to the client to get the animation
             // started. Cancel any existing recents animation running synchronously (do not hold the
             // WM lock)
-            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
+            mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION,
                     "startRecentsActivity");
             mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
                     this, mDefaultDisplay.mDisplayId,
@@ -396,12 +396,8 @@
 
     @Override
     public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
-            boolean runSychronously, boolean sendUserLeaveHint) {
-        if (runSychronously) {
-            finishAnimation(reorderMode, sendUserLeaveHint);
-        } else {
-            mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint));
-        }
+            boolean sendUserLeaveHint) {
+        finishAnimation(reorderMode, sendUserLeaveHint);
     }
 
     @Override
@@ -435,8 +431,7 @@
         } else {
             // Just cancel directly to unleash from launcher when the next launching task is the
             // current top task.
-            mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
-                    "stackOrderChanged");
+            mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "stackOrderChanged");
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 724a72e..8752f37 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -168,8 +168,7 @@
 
     public interface RecentsAnimationCallbacks {
         /** Callback when recents animation is finished. */
-        void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously,
-                boolean sendUserLeaveHint);
+        void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint);
     }
 
     private final IRecentsAnimationController mController =
@@ -221,8 +220,7 @@
                 // prior to calling the callback
                 mCallbacks.onAnimationFinished(moveHomeToTop
                         ? REORDER_MOVE_TO_TOP
-                        : REORDER_MOVE_TO_ORIGINAL_POSITION,
-                        true /* runSynchronously */, sendUserLeaveHint);
+                        : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
                 mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -498,21 +496,15 @@
     }
 
     void cancelAnimation(@ReorderMode int reorderMode, String reason) {
-        cancelAnimation(reorderMode, false /* runSynchronously */, false /*screenshot */, reason);
-    }
-
-    void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
-        cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason);
+        cancelAnimation(reorderMode, false /*screenshot */, reason);
     }
 
     void cancelAnimationWithScreenshot(boolean screenshot) {
-        cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, screenshot, "stackOrderChanged");
+        cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "stackOrderChanged");
     }
 
-    private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
-            boolean screenshot, String reason) {
-        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason
-                + " runSynchronously=" + runSynchronously);
+    private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
         synchronized (mService.getWindowManagerLock()) {
             if (mCanceled) {
                 // We've already canceled the animation
@@ -525,16 +517,14 @@
                 // Screen shot previous task when next task starts transition and notify the runner.
                 // We will actually finish the animation once the runner calls cleanUpScreenshot().
                 final Task task = mPendingAnimations.get(0).mTask;
-                final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode,
-                        runSynchronously);
+                final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode);
                 try {
                     mRunner.onAnimationCanceled(taskSnapshot);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to cancel recents animation", e);
                 }
                 if (taskSnapshot == null) {
-                    mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                            false /* sendUserLeaveHint */);
+                    mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
                 }
             } else {
                 // Otherwise, notify the runner and clean up the animation immediately
@@ -545,8 +535,7 @@
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Failed to cancel recents animation", e);
                 }
-                mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                        false /* sendUserLeaveHint */);
+                mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
             }
         }
     }
@@ -592,8 +581,7 @@
         return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
     }
 
-    TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode,
-            boolean runSynchronously) {
+    TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) {
         final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
         final ArraySet<Task> tasks = Sets.newArraySet(task);
         snapshotController.snapshotTasks(tasks);
@@ -613,8 +601,7 @@
                     if (DEBUG_RECENTS_ANIMATIONS) {
                         Slog.d(TAG, "mRecentScreenshotAnimator finish");
                     }
-                    mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
-                            false /* sendUserLeaveHint */);
+                    mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
                 }, mService);
         mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
         return taskSnapshot;
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 66d42db..3401de6 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -227,15 +227,10 @@
         mStackSupervisor.mRootActivityContainer = this;
     }
 
-    @VisibleForTesting
-    void setWindowContainer(RootWindowContainer container) {
-        mRootWindowContainer = container;
-        mRootWindowContainer.setRootActivityContainer(this);
-    }
-
     void setWindowManager(WindowManagerService wm) {
         mWindowManager = wm;
-        setWindowContainer(mWindowManager.mRoot);
+        mRootWindowContainer = mWindowManager.mRoot;
+        mRootWindowContainer.setRootActivityContainer(this);
         mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
         mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2266,7 +2261,7 @@
             @WindowConfiguration.ActivityType int ignoreActivityType,
             @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
             boolean allowed) {
-        mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+        mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
                 ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fd86faa..3a2eb57 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -380,7 +380,7 @@
 
     boolean isResizeable() {
         return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
-                || mWmService.mForceResizableTasks;
+                || mWmService.mAtmService.mForceResizableActivities;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 79367a0..cc2112e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1666,7 +1666,7 @@
      *         default bounds.
      */
     Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
-        if (!mWmService.mSupportsPictureInPicture) {
+        if (!mWmService.mAtmService.mSupportsPictureInPicture) {
             return null;
         }
 
@@ -1762,7 +1762,7 @@
      * Sets the current picture-in-picture aspect ratio.
      */
     void setPictureInPictureAspectRatio(float aspectRatio) {
-        if (!mWmService.mSupportsPictureInPicture) {
+        if (!mWmService.mAtmService.mSupportsPictureInPicture) {
             return;
         }
 
@@ -1792,7 +1792,7 @@
      * Sets the current picture-in-picture actions.
      */
     void setPictureInPictureActions(List<RemoteAction> actions) {
-        if (!mWmService.mSupportsPictureInPicture) {
+        if (!mWmService.mAtmService.mSupportsPictureInPicture) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bbef261..29d232f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -767,11 +767,11 @@
      */
     void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
             @Nullable ConfigurationContainer requestingContainer) {
-        final boolean changed = mOrientation != orientation;
-        mOrientation = orientation;
-        if (!changed) {
+        if (mOrientation == orientation) {
             return;
         }
+
+        mOrientation = orientation;
         final WindowContainer parent = getParent();
         if (parent != null) {
             onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 86faad0..d8d6841 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -28,6 +28,7 @@
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myPid;
@@ -591,9 +592,6 @@
     int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     Rect mDockedStackCreateBounds;
 
-    boolean mForceResizableTasks;
-    boolean mSupportsPictureInPicture;
-    boolean mSupportsFreeformWindowManagement;
     boolean mIsPc;
     /**
      * Flag that indicates that desktop mode is forced for public secondary screens.
@@ -819,7 +817,7 @@
     int mTransactionSequence;
 
     final WindowAnimator mAnimator;
-    final SurfaceAnimationRunner mSurfaceAnimationRunner;
+    SurfaceAnimationRunner mSurfaceAnimationRunner;
 
     /**
      * Keeps track of which animations got transferred to which animators. Entries will get cleaned
@@ -957,6 +955,9 @@
 
     final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>();
 
+    @VisibleForTesting
+    final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+
     interface AppFreezeListener {
         void onAppFreezeTimeout();
     }
@@ -1010,6 +1011,7 @@
         mGlobalLock = atm.getGlobalLock();
         mAtmService = atm;
         mContext = context;
+        mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
         mAllowBootMessages = showBootMsgs;
         mOnlyCore = onlyCore;
         mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -1159,26 +1161,28 @@
         mSystemGestureExcludedByPreQStickyImmersive =
                 DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
                         KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                new HandlerExecutor(mH), properties -> {
-                    synchronized (mGlobalLock) {
-                        final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
-                                properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
-                        final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
-                                DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                                KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
-                        if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
-                                || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
-                            mSystemGestureExclusionLimitDp = exclusionLimitDp;
-                            mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
-                            mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
-                        }
 
-                        mSystemGestureExclusionLogDebounceTimeoutMillis =
-                                DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                                        KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
-                    }
-                });
+        mPropertiesChangedListener = properties -> {
+            synchronized (mGlobalLock) {
+                final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+                        properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+                final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
+                        DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                        KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
+                if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
+                        || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+                    mSystemGestureExclusionLimitDp = exclusionLimitDp;
+                    mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
+                    mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
+                }
+
+                mSystemGestureExclusionLogDebounceTimeoutMillis =
+                        DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                                KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
+            }
+        };
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+                new HandlerExecutor(mH), mPropertiesChangedListener);
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
     }
@@ -2595,7 +2599,7 @@
         mRecentsAnimationController = controller;
     }
 
-    public RecentsAnimationController getRecentsAnimationController() {
+    RecentsAnimationController getRecentsAnimationController() {
         return mRecentsAnimationController;
     }
 
@@ -2603,74 +2607,37 @@
      * @return Whether the next recents animation can continue to start. Called from
      *         {@link RecentsAnimation#startRecentsActivity}.
      */
-    public boolean canStartRecentsAnimation() {
-        synchronized (mGlobalLock) {
-            // TODO(multi-display): currently only default display support recent activity
-            if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
-                return false;
-            }
-            return true;
+    boolean canStartRecentsAnimation() {
+        // TODO(multi-display): currently only default display support recent activity
+        if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
+            return false;
         }
+        return true;
     }
 
-    /**
-     * Cancels any running recents animation. The caller should NOT hold the WM lock while calling
-     * this method, as it will call back into AM and may cause a deadlock. Any locking will be done
-     * in the animation controller itself.
-     */
-    public void cancelRecentsAnimationSynchronously(
+    void cancelRecentsAnimation(
             @RecentsAnimationController.ReorderMode int reorderMode, String reason) {
         if (mRecentsAnimationController != null) {
             // This call will call through to cleanupAnimation() below after the animation is
             // canceled
-            mRecentsAnimationController.cancelAnimationSynchronously(reorderMode, reason);
+            mRecentsAnimationController.cancelAnimation(reorderMode, reason);
         }
     }
 
-    public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
-        synchronized (mGlobalLock) {
-            if (mRecentsAnimationController != null) {
-                final RecentsAnimationController controller = mRecentsAnimationController;
-                mRecentsAnimationController = null;
-                controller.cleanupAnimation(reorderMode);
-                // TODO(mult-display): currently only default display support recents animation.
-                getDefaultDisplayContentLocked().mAppTransition.updateBooster();
-            }
+    void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+        if (mRecentsAnimationController != null) {
+            final RecentsAnimationController controller = mRecentsAnimationController;
+            mRecentsAnimationController = null;
+            controller.cleanupAnimation(reorderMode);
+            // TODO(mult-display): currently only default display support recents animation.
+            getDefaultDisplayContentLocked().mAppTransition.updateBooster();
         }
     }
 
-    public void setAppFullscreen(IBinder token, boolean toOpaque) {
-        synchronized (mGlobalLock) {
-            final AppWindowToken atoken = mRoot.getAppWindowToken(token);
-            if (atoken != null) {
-                atoken.setFillsParent(toOpaque);
-                setWindowOpaqueLocked(token, toOpaque);
-                mWindowPlacerLocked.requestTraversal();
-            }
-        }
-    }
-
-    public void setWindowOpaque(IBinder token, boolean isOpaque) {
-        synchronized (mGlobalLock) {
-            setWindowOpaqueLocked(token, isOpaque);
-        }
-    }
-
-    private void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
+    void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
         final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
         if (wtoken != null) {
-            final WindowState win = wtoken.findMainWindow();
-            if (win == null) {
-                return;
-            }
-            isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
-            win.mWinAnimator.setOpaqueLocked(isOpaque);
-        }
-    }
-
-    public void setDockedStackCreateState(int mode, Rect bounds) {
-        synchronized (mGlobalLock) {
-            setDockedStackCreateStateLocked(mode, bounds);
+            wtoken.setMainWindowOpaque(isOpaque);
         }
     }
 
@@ -2679,14 +2646,12 @@
         mDockedStackCreateBounds = bounds;
     }
 
-    public void checkSplitScreenMinimizedChanged(boolean animate) {
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDefaultDisplayContentLocked();
-            displayContent.getDockedDividerController().checkMinimizeChanged(animate);
-        }
+    void checkSplitScreenMinimizedChanged(boolean animate) {
+        final DisplayContent displayContent = getDefaultDisplayContentLocked();
+        displayContent.getDockedDividerController().checkMinimizeChanged(animate);
     }
 
-    public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
+    boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
         final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
         return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
                 aspectRatio);
@@ -4847,8 +4812,10 @@
                 case UPDATE_DOCKED_STACK_DIVIDER: {
                     synchronized (mGlobalLock) {
                         final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                        displayContent.getDockedDividerController().reevaluateVisibility(false);
-                        displayContent.adjustForImeIfNeeded();
+                        if (displayContent != null) {
+                            displayContent.getDockedDividerController().reevaluateVisibility(false);
+                            displayContent.adjustForImeIfNeeded();
+                        }
                     }
                     break;
                 }
@@ -6495,31 +6462,14 @@
         }
     }
 
-    public void setForceResizableTasks(boolean forceResizableTasks) {
-        synchronized (mGlobalLock) {
-            mForceResizableTasks = forceResizableTasks;
-        }
-    }
-
-    public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
-        synchronized (mGlobalLock) {
-            mSupportsPictureInPicture = supportsPictureInPicture;
-        }
-    }
-
-    public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
-        synchronized (mGlobalLock) {
-            mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
-        }
-    }
-
     void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
         synchronized (mGlobalLock) {
             mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
         }
     }
 
-    public void setIsPc(boolean isPc) {
+    @VisibleForTesting
+    void setIsPc(boolean isPc) {
         synchronized (mGlobalLock) {
             mIsPc = isPc;
         }
@@ -6546,7 +6496,7 @@
                 "registerPinnedStackListener()")) {
             return;
         }
-        if (!mSupportsPictureInPicture) {
+        if (!mAtmService.mSupportsPictureInPicture) {
             return;
         }
         synchronized (mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e14514b..d87a0ed 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5125,9 +5125,17 @@
             // relative layering of multiple APPLICATION_MEDIA/OVERLAY has never
             // been defined and so we can use static layers and leave it that way.
             if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) {
-                w.assignLayer(t, -2);
+                if (mWinAnimator.hasSurface()) {
+                    w.assignRelativeLayer(t, mWinAnimator.mSurfaceController.mSurfaceControl, -2);
+                } else {
+                    w.assignLayer(t, -2);
+                }
             } else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
-                w.assignLayer(t, -1);
+                if (mWinAnimator.hasSurface()) {
+                    w.assignRelativeLayer(t, mWinAnimator.mSurfaceController.mSurfaceControl, -1);
+                } else {
+                    w.assignLayer(t, -1);
+                }
             } else {
                 w.assignLayer(t, layer);
             }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 466ca93..03f4755 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -165,6 +165,7 @@
             outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
                     bitmapCopy->rowBytes(), 0, 0);
         }
+        outSpriteIcon->style = outPointerIcon->style;
         outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
         outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
     }
@@ -1252,7 +1253,8 @@
     status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
             displayContext.get(), &pointerIcon);
     if (!status && !pointerIcon.isNullIcon()) {
-        *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
+        *icon = SpriteIcon(
+                pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
     } else {
         *icon = SpriteIcon();
     }
@@ -1293,10 +1295,12 @@
                     milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
             animationData.animationFrames.reserve(numFrames);
             animationData.animationFrames.push_back(SpriteIcon(
-                    pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                    pointerIcon.bitmap, pointerIcon.style,
+                    pointerIcon.hotSpotX, pointerIcon.hotSpotY));
             for (size_t i = 0; i < numFrames - 1; ++i) {
               animationData.animationFrames.push_back(SpriteIcon(
-                      pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+                      pointerIcon.bitmapFrames[i], pointerIcon.style,
+                      pointerIcon.hotSpotX, pointerIcon.hotSpotY));
             }
         }
     }
@@ -1711,6 +1715,7 @@
         pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
                 spriteIcon.bitmap.rowBytes(), 0, 0);
     }
+    spriteIcon.style = pointerIcon.style;
     spriteIcon.hotSpotX = pointerIcon.hotSpotX;
     spriteIcon.hotSpotY = pointerIcon.hotSpotY;
     im->setCustomPointerIcon(spriteIcon);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f800cca..f933b09 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10846,6 +10846,12 @@
     public void setGlobalSetting(ComponentName who, String setting, String value) {
         Preconditions.checkNotNull(who, "ComponentName is null");
 
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING)
+                .setAdmin(who)
+                .setStrings(setting, value)
+                .write();
+
         synchronized (getLockObject()) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 83b3194..67ae407 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -25,6 +25,7 @@
 import static com.android.server.utils.TimingsTraceAndSlog.SYSTEM_SERVER_TIMING_TAG;
 
 import android.annotation.NonNull;
+import android.annotation.StringRes;
 import android.app.ActivityThread;
 import android.app.INotificationManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -39,7 +40,9 @@
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
 import android.hardware.display.DisplayManagerInternal;
+import android.net.ConnectivityModuleConnector;
 import android.net.NetworkStackClient;
+import android.net.wifi.WifiStackClient;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -632,6 +635,13 @@
         SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
         t.traceEnd();
 
+        // Platform compat service is used by ActivityManagerService, PackageManagerService, and
+        // possibly others in the future. b/135010838.
+        t.traceBegin("PlatformCompat");
+        ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
+                new PlatformCompat(mSystemContext));
+        t.traceEnd();
+
         // Wait for installd to finish starting up so that it has a chance to
         // create critical directories such as /data/user with the appropriate
         // permissions.  We need this to complete before we initialize other services.
@@ -1099,10 +1109,6 @@
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
 
-            t.traceBegin("PlatformCompat");
-            ServiceManager.addService("platform_compat", new PlatformCompat(context));
-            t.traceEnd();
-
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
@@ -1265,13 +1271,29 @@
             startSystemCaptionsManagerService(context, t);
 
             // App prediction manager service
-            t.traceBegin("StartAppPredictionService");
-            mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
-            t.traceEnd();
+            if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
+                t.traceBegin("StartAppPredictionService");
+                mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG, "AppPredictionService not defined by OEM");
+            }
 
             // Content suggestions manager service
-            t.traceBegin("StartContentSuggestionsService");
-            mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS);
+            if (deviceHasConfigString(context, R.string.config_defaultContentSuggestionsService)) {
+                t.traceBegin("StartContentSuggestionsService");
+                mSystemServiceManager.startService(CONTENT_SUGGESTIONS_SERVICE_CLASS);
+                t.traceEnd();
+            } else {
+                Slog.d(TAG, "ContentSuggestionsService not defined by OEM");
+            }
+
+            t.traceBegin("InitConnectivityModuleConnector");
+            try {
+                ConnectivityModuleConnector.getInstance().init(context);
+            } catch (Throwable e) {
+                reportWtf("initializing ConnectivityModuleConnector", e);
+            }
             t.traceEnd();
 
             t.traceBegin("InitNetworkStackClient");
@@ -1336,40 +1358,6 @@
             t.traceEnd();
 
             if (context.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WIFI)) {
-                // Wifi Service must be started first for wifi-related services.
-                t.traceBegin("StartWifi");
-                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                t.traceEnd();
-                t.traceBegin("StartWifiScanning");
-                mSystemServiceManager.startService(
-                        "com.android.server.wifi.scanner.WifiScanningService");
-                t.traceEnd();
-            }
-
-            if (context.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WIFI_RTT)) {
-                t.traceBegin("StartRttService");
-                mSystemServiceManager.startService(
-                        "com.android.server.wifi.rtt.RttService");
-                t.traceEnd();
-            }
-
-            if (context.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WIFI_AWARE)) {
-                t.traceBegin("StartWifiAware");
-                mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
-                t.traceEnd();
-            }
-
-            if (context.getPackageManager().hasSystemFeature(
-                    PackageManager.FEATURE_WIFI_DIRECT)) {
-                t.traceBegin("StartWifiP2P");
-                mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
-                t.traceEnd();
-            }
-
-            if (context.getPackageManager().hasSystemFeature(
                     PackageManager.FEATURE_LOWPAN)) {
                 t.traceBegin("StartLowpan");
                 mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
@@ -2168,12 +2156,21 @@
                 // ActivityManagerService.mSystemReady and ActivityManagerService.mProcessesReady
                 // are set to true. Be careful if moving this to a different place in the
                 // startup sequence.
-                NetworkStackClient.getInstance().start(context);
+                NetworkStackClient.getInstance().start();
             } catch (Throwable e) {
                 reportWtf("starting Network Stack", e);
             }
             t.traceEnd();
 
+            t.traceBegin("StartWifiStack");
+            try {
+                WifiStackClient.getInstance().start();
+            } catch (Throwable e) {
+                reportWtf("starting Wifi Stack", e);
+            }
+            t.traceEnd();
+
+
             t.traceBegin("MakeLocationServiceReady");
             try {
                 if (locationF != null) {
@@ -2257,11 +2254,14 @@
         t.traceEnd(); // startOtherServices
     }
 
+    private boolean deviceHasConfigString(@NonNull Context context, @StringRes int resId) {
+        String serviceName = context.getString(resId);
+        return !TextUtils.isEmpty(serviceName);
+    }
+
     private void startSystemCaptionsManagerService(@NonNull Context context,
             @NonNull TimingsTraceAndSlog t) {
-        String serviceName = context.getString(
-                com.android.internal.R.string.config_defaultSystemCaptionsManagerService);
-        if (TextUtils.isEmpty(serviceName)) {
+        if (!deviceHasConfigString(context, R.string.config_defaultSystemCaptionsManagerService)) {
             Slog.d(TAG, "SystemCaptionsManagerService disabled because resource is not overlaid");
             return;
         }
@@ -2289,9 +2289,7 @@
 
         // Then check if OEM overlaid the resource that defines the service.
         if (!explicitlyEnabled) {
-            final String serviceName = context
-                    .getString(com.android.internal.R.string.config_defaultContentCaptureService);
-            if (TextUtils.isEmpty(serviceName)) {
+            if (!deviceHasConfigString(context, R.string.config_defaultContentCaptureService)) {
                 Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid");
                 return;
             }
diff --git a/services/net/java/android/net/ConnectivityModuleConnector.java b/services/net/java/android/net/ConnectivityModuleConnector.java
new file mode 100644
index 0000000..7333f58
--- /dev/null
+++ b/services/net/java/android/net/ConnectivityModuleConnector.java
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2019 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.net;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.net.util.SharedLog;
+import android.os.Build;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.text.format.DateUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.PrintWriter;
+
+/**
+ * Class used to communicate to the various networking mainline modules running in the network stack
+ * process from {@link com.android.server.SystemServer}.
+ * @hide
+ */
+public class ConnectivityModuleConnector {
+    private static final String TAG = ConnectivityModuleConnector.class.getSimpleName();
+    private static final String IN_PROCESS_SUFFIX = ".InProcess";
+
+    private static final String PREFS_FILE = "ConnectivityModuleConnector.xml";
+    private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
+    private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
+    private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
+    private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
+            "always_ratelimit_networkstack_crash";
+
+    // Even if the network stack is lost, do not crash the system more often than this.
+    // Connectivity would be broken, but if the user needs the device for something urgent
+    // (like calling emergency services) we should not bootloop the device.
+    // This is the default value: the actual value can be adjusted via device config.
+    private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
+
+    // Even if the network stack is lost, do not crash the system server if it was less than
+    // this much after boot. This avoids bootlooping the device, and crashes should address very
+    // infrequent failures, not failures on boot.
+    private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
+
+    private static ConnectivityModuleConnector sInstance;
+
+    private Context mContext;
+    @GuardedBy("mLog")
+    private final SharedLog mLog = new SharedLog(TAG);
+    @GuardedBy("mHealthListeners")
+    private final ArraySet<ConnectivityModuleHealthListener> mHealthListeners = new ArraySet<>();
+
+    private ConnectivityModuleConnector() { }
+
+    /**
+     * Get the {@link ConnectivityModuleConnector} singleton instance.
+     */
+    public static synchronized ConnectivityModuleConnector getInstance() {
+        if (sInstance == null) {
+            sInstance = new ConnectivityModuleConnector();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Initialize the network stack connector. Should be called only once on device startup, before
+     * any client attempts to use the network stack.
+     */
+    public void init(Context context) {
+        log("Network stack init");
+        mContext = context;
+    }
+
+    /**
+     * Callback interface for severe failures of the NetworkStack.
+     *
+     * <p>Useful for health monitors such as PackageWatchdog.
+     */
+    public interface ConnectivityModuleHealthListener {
+        /**
+         * Called when there is a severe failure of the network stack.
+         * @param packageName Package name of the network stack.
+         */
+        void onNetworkStackFailure(@NonNull String packageName);
+    }
+
+    /**
+     * Callback invoked by the connector once the connection to the corresponding module is
+     * established.
+     */
+    public interface ModuleServiceCallback {
+        /**
+         * Invoked when the corresponding service has connected.
+         *
+         * @param iBinder Binder object for the service.
+         */
+        void onModuleServiceConnected(@NonNull IBinder iBinder);
+    }
+
+
+    /**
+     * Add a {@link ConnectivityModuleHealthListener} to listen to network stack health events.
+     */
+    public void registerHealthListener(@NonNull ConnectivityModuleHealthListener listener) {
+        synchronized (mHealthListeners) {
+            mHealthListeners.add(listener);
+        }
+    }
+
+    /**
+     * Start a module running in the network stack or system_server process. Should be called only
+     * once for each module per device startup.
+     *
+     * <p>This method will start a networking module either in the network stack
+     * process, or inside the system server on devices that do not support the corresponding
+     * mainline network . The corresponding networking module service's binder
+     * object will then be delivered asynchronously via the provided {@link ModuleServiceCallback}.
+     *
+     * @param serviceIntentBaseAction Base action to use for constructing the intent needed to
+     *                                bind to the corresponding module.
+     * @param servicePermissionName Permission to be held by the corresponding module.
+     */
+    public void startModuleService(
+            @NonNull String serviceIntentBaseAction,
+            @NonNull String servicePermissionName,
+            @NonNull ModuleServiceCallback callback) {
+        log("Starting networking module " + serviceIntentBaseAction);
+
+        final PackageManager pm = mContext.getPackageManager();
+
+        // Try to bind in-process if the device was shipped with an in-process version
+        Intent intent = getModuleServiceIntent(pm, serviceIntentBaseAction, servicePermissionName,
+                true /* inSystemProcess */);
+
+        // Otherwise use the updatable module version
+        if (intent == null) {
+            intent = getModuleServiceIntent(pm, serviceIntentBaseAction, servicePermissionName,
+                false /* inSystemProcess */);
+            log("Starting networking module in network_stack process");
+        } else {
+            log("Starting networking module in system_server process");
+        }
+
+        if (intent == null) {
+            maybeCrashWithTerribleFailure("Could not resolve the networking module", null);
+            return;
+        }
+
+        final String packageName = intent.getComponent().getPackageName();
+
+        // Start the network stack. The service will be added to the service manager by the
+        // corresponding client in ModuleServiceCallback.onModuleServiceConnected().
+        if (!mContext.bindServiceAsUser(
+                intent, new ModuleServiceConnection(packageName, callback),
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+            maybeCrashWithTerribleFailure(
+                    "Could not bind to networking module in-process, or in app with "
+                            + intent, packageName);
+            return;
+        }
+
+        log("Networking module service start requested");
+    }
+
+    private class ModuleServiceConnection implements ServiceConnection {
+        @NonNull
+        private final String mPackageName;
+        @NonNull
+        private final ModuleServiceCallback mModuleServiceCallback;
+
+        private ModuleServiceConnection(
+                @NonNull String packageName,
+                @NonNull ModuleServiceCallback moduleCallback) {
+            mPackageName = packageName;
+            mModuleServiceCallback = moduleCallback;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            logi("Networking module service connected");
+            mModuleServiceCallback.onModuleServiceConnected(service);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            // onServiceDisconnected is not being called on device shutdown, so this method being
+            // called always indicates a bad state for the system server.
+            // This code path is only run by the system server: only the system server binds
+            // to the NetworkStack as a service. Other processes get the NetworkStack from
+            // the ServiceManager.
+            maybeCrashWithTerribleFailure("Lost network stack", mPackageName);
+        }
+    }
+
+    @Nullable
+    private Intent getModuleServiceIntent(
+            @NonNull PackageManager pm, @NonNull String serviceIntentBaseAction,
+            @NonNull String servicePermissionName, boolean inSystemProcess) {
+        final Intent intent =
+                new Intent(inSystemProcess
+                        ? serviceIntentBaseAction + IN_PROCESS_SUFFIX
+                        : serviceIntentBaseAction);
+        final ComponentName comp = intent.resolveSystemService(pm, 0);
+        if (comp == null) {
+            return null;
+        }
+        intent.setComponent(comp);
+
+        int uid = -1;
+        try {
+            uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
+        } catch (PackageManager.NameNotFoundException e) {
+            logWtf("Networking module package not found", e);
+            // Fall through
+        }
+
+        final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
+        if (uid != expectedUid) {
+            throw new SecurityException("Invalid network stack UID: " + uid);
+        }
+
+        if (!inSystemProcess) {
+            checkModuleServicePermission(pm, comp, servicePermissionName);
+        }
+
+        return intent;
+    }
+
+    private void checkModuleServicePermission(
+            @NonNull PackageManager pm, @NonNull ComponentName comp,
+            @NonNull String servicePermissionName) {
+        final int hasPermission =
+                pm.checkPermission(servicePermissionName, comp.getPackageName());
+        if (hasPermission != PERMISSION_GRANTED) {
+            throw new SecurityException(
+                    "Networking module does not have permission " + servicePermissionName);
+        }
+    }
+
+    private synchronized void maybeCrashWithTerribleFailure(@NonNull String message,
+            @Nullable String packageName) {
+        logWtf(message, null);
+        // uptime is monotonic even after a framework restart
+        final long uptime = SystemClock.elapsedRealtime();
+        final long now = System.currentTimeMillis();
+        final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+                CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
+        final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
+                CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
+        final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
+                CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
+
+        final SharedPreferences prefs = getSharedPreferences();
+        final long lastCrashTime = tryGetLastCrashTime(prefs);
+
+        // Only crash if there was enough time since boot, and (if known) enough time passed since
+        // the last crash.
+        // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
+        // are only used to limit the number of crashes compared to only using the time since boot,
+        // which would also be OK behavior by itself.
+        // - If lastCrashTime is incorrectly more than the current time, only look at uptime
+        // - If it is much less than current time, only look at uptime
+        // - If current time is during the next few hours after last crash time, don't crash.
+        //   Considering that this only matters if last boot was some time ago, it's likely that
+        //   time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
+        //   in this last state would also not last for long since the window is only a few hours.
+        final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
+        final boolean justBooted = uptime < minUptimeBeforeCrash;
+        final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
+        final boolean haveKnownRecentCrash =
+                haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
+        if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
+            // The system is not bound to its network stack (for example due to a crash in the
+            // network stack process): better crash rather than stay in a bad state where all
+            // networking is broken.
+            // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
+            // API to persist settings before a crash.
+            tryWriteLastCrashTime(prefs, now);
+            throw new IllegalStateException(message);
+        }
+
+        // Here the system crashed recently already. Inform listeners that something is
+        // definitely wrong.
+        if (packageName != null) {
+            final ArraySet<ConnectivityModuleHealthListener> listeners;
+            synchronized (mHealthListeners) {
+                listeners = new ArraySet<>(mHealthListeners);
+            }
+            for (ConnectivityModuleHealthListener listener : listeners) {
+                listener.onNetworkStackFailure(packageName);
+            }
+        }
+    }
+
+    @Nullable
+    private SharedPreferences getSharedPreferences() {
+        try {
+            final File prefsFile = new File(
+                    Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
+            return mContext.createDeviceProtectedStorageContext()
+                    .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
+        } catch (Throwable e) {
+            logWtf("Error loading shared preferences", e);
+            return null;
+        }
+    }
+
+    private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
+        if (prefs == null) return 0L;
+        try {
+            return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
+        } catch (Throwable e) {
+            logWtf("Error getting last crash time", e);
+            return 0L;
+        }
+    }
+
+    private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
+        if (prefs == null) return;
+        try {
+            prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
+        } catch (Throwable e) {
+            logWtf("Error writing last crash time", e);
+        }
+    }
+
+    private void log(@NonNull String message) {
+        Slog.d(TAG, message);
+        synchronized (mLog) {
+            mLog.log(message);
+        }
+    }
+
+    private void logWtf(@NonNull String message, @Nullable Throwable e) {
+        Slog.wtf(TAG, message, e);
+        synchronized (mLog) {
+            mLog.e(message);
+        }
+    }
+
+    private void loge(@NonNull String message, @Nullable Throwable e) {
+        Slog.e(TAG, message, e);
+        synchronized (mLog) {
+            mLog.e(message);
+        }
+    }
+
+    private void logi(@NonNull String message) {
+        Slog.i(TAG, message);
+        synchronized (mLog) {
+            mLog.i(message);
+        }
+    }
+
+    /**
+     * Dump ConnectivityModuleConnector logs to the specified {@link PrintWriter}.
+     */
+    public void dump(PrintWriter pw) {
+        // dump is thread-safe on SharedLog
+        mLog.dump(null, pw, null);
+    }
+}
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
index 56b728c..abb4666 100644
--- a/services/net/java/android/net/NetworkStackClient.java
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -15,40 +15,27 @@
  */
 package android.net;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.net.dhcp.DhcpServingParamsParcel;
 import android.net.dhcp.IDhcpServerCallbacks;
 import android.net.ip.IIpClientCallbacks;
 import android.net.util.SharedLog;
 import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.DeviceConfig;
-import android.text.format.DateUtils;
-import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.io.File;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -60,24 +47,6 @@
     private static final String TAG = NetworkStackClient.class.getSimpleName();
 
     private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
-    private static final String IN_PROCESS_SUFFIX = ".InProcess";
-    private static final String PREFS_FILE = "NetworkStackClientPrefs.xml";
-    private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time";
-    private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval";
-    private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash";
-    private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH =
-            "always_ratelimit_networkstack_crash";
-
-    // Even if the network stack is lost, do not crash the system more often than this.
-    // Connectivity would be broken, but if the user needs the device for something urgent
-    // (like calling emergency services) we should not bootloop the device.
-    // This is the default value: the actual value can be adjusted via device config.
-    private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS;
-
-    // Even if the network stack is lost, do not crash the system server if it was less than
-    // this much after boot. This avoids bootlooping the device, and crashes should address very
-    // infrequent failures, not failures on boot.
-    private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS;
 
     private static NetworkStackClient sInstance;
 
@@ -93,26 +62,10 @@
 
     private volatile boolean mWasSystemServerInitialized = false;
 
-    @GuardedBy("mHealthListeners")
-    private final ArraySet<NetworkStackHealthListener> mHealthListeners = new ArraySet<>();
-
     private interface NetworkStackCallback {
         void onNetworkStackConnected(INetworkStackConnector connector);
     }
 
-    /**
-     * Callback interface for severe failures of the NetworkStack.
-     *
-     * <p>Useful for health monitors such as PackageWatchdog.
-     */
-    public interface NetworkStackHealthListener {
-        /**
-         * Called when there is a severe failure of the network stack.
-         * @param packageName Package name of the network stack.
-         */
-        void onNetworkStackFailure(@NonNull String packageName);
-    }
-
     private NetworkStackClient() { }
 
     /**
@@ -126,15 +79,6 @@
     }
 
     /**
-     * Add a {@link NetworkStackHealthListener} to listen to network stack health events.
-     */
-    public void registerHealthListener(@NonNull NetworkStackHealthListener listener) {
-        synchronized (mHealthListeners) {
-            mHealthListeners.add(listener);
-        }
-    }
-
-    /**
      * Create a DHCP server according to the specified parameters.
      *
      * <p>The server will be returned asynchronously through the provided callbacks.
@@ -195,32 +139,13 @@
         });
     }
 
-    private class NetworkStackConnection implements ServiceConnection {
-        @NonNull
-        private final Context mContext;
-        @NonNull
-        private final String mPackageName;
-
-        private NetworkStackConnection(@NonNull Context context, @NonNull String packageName) {
-            mContext = context;
-            mPackageName = packageName;
-        }
-
+    private class NetworkStackConnection implements
+            ConnectivityModuleConnector.ModuleServiceCallback {
         @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
+        public void onModuleServiceConnected(IBinder service) {
             logi("Network stack service connected");
             registerNetworkStackService(service);
         }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            // onServiceDisconnected is not being called on device shutdown, so this method being
-            // called always indicates a bad state for the system server.
-            // This code path is only run by the system server: only the system server binds
-            // to the NetworkStack as a service. Other processes get the NetworkStack from
-            // the ServiceManager.
-            maybeCrashWithTerribleFailure("Lost network stack", mContext, mPackageName);
-        }
     }
 
     private void registerNetworkStackService(@NonNull IBinder service) {
@@ -259,171 +184,13 @@
      * connector will then be delivered asynchronously to clients that requested it before it was
      * started.
      */
-    public void start(Context context) {
-        log("Starting network stack");
-
-        final PackageManager pm = context.getPackageManager();
-
-        // Try to bind in-process if the device was shipped with an in-process version
-        Intent intent = getNetworkStackIntent(pm, true /* inSystemProcess */);
-
-        // Otherwise use the updatable module version
-        if (intent == null) {
-            intent = getNetworkStackIntent(pm, false /* inSystemProcess */);
-            log("Starting network stack process");
-        } else {
-            log("Starting network stack in-process");
-        }
-
-        if (intent == null) {
-            maybeCrashWithTerribleFailure("Could not resolve the network stack", context, null);
-            return;
-        }
-
-        final String packageName = intent.getComponent().getPackageName();
-
-        // Start the network stack. The service will be added to the service manager in
-        // NetworkStackConnection.onServiceConnected().
-        if (!context.bindServiceAsUser(intent, new NetworkStackConnection(context, packageName),
-                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
-            maybeCrashWithTerribleFailure(
-                    "Could not bind to network stack in-process, or in app with " + intent,
-                    context, packageName);
-            return;
-        }
-
+    public void start() {
+        ConnectivityModuleConnector.getInstance().startModuleService(
+                INetworkStackConnector.class.getName(), PERMISSION_MAINLINE_NETWORK_STACK,
+                new NetworkStackConnection());
         log("Network stack service start requested");
     }
 
-    @Nullable
-    private Intent getNetworkStackIntent(@NonNull PackageManager pm, boolean inSystemProcess) {
-        final String baseAction = INetworkStackConnector.class.getName();
-        final Intent intent =
-                new Intent(inSystemProcess ? baseAction + IN_PROCESS_SUFFIX : baseAction);
-        final ComponentName comp = intent.resolveSystemService(pm, 0);
-
-        if (comp == null) {
-            return null;
-        }
-        intent.setComponent(comp);
-
-        int uid = -1;
-        try {
-            uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM);
-        } catch (PackageManager.NameNotFoundException e) {
-            logWtf("Network stack package not found", e);
-            // Fall through
-        }
-
-        final int expectedUid = inSystemProcess ? Process.SYSTEM_UID : Process.NETWORK_STACK_UID;
-        if (uid != expectedUid) {
-            throw new SecurityException("Invalid network stack UID: " + uid);
-        }
-
-        if (!inSystemProcess) {
-            checkNetworkStackPermission(pm, comp);
-        }
-
-        return intent;
-    }
-
-    private void checkNetworkStackPermission(
-            @NonNull PackageManager pm, @NonNull ComponentName comp) {
-        final int hasPermission =
-                pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
-        if (hasPermission != PERMISSION_GRANTED) {
-            throw new SecurityException(
-                    "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
-        }
-    }
-
-    private void maybeCrashWithTerribleFailure(@NonNull String message,
-            @NonNull Context context, @Nullable String packageName) {
-        logWtf(message, null);
-        // uptime is monotonic even after a framework restart
-        final long uptime = SystemClock.elapsedRealtime();
-        final long now = System.currentTimeMillis();
-        final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
-                CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS);
-        final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY,
-                CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS);
-        final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY,
-                CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false);
-
-        final SharedPreferences prefs = getSharedPreferences(context);
-        final long lastCrashTime = tryGetLastCrashTime(prefs);
-
-        // Only crash if there was enough time since boot, and (if known) enough time passed since
-        // the last crash.
-        // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they
-        // are only used to limit the number of crashes compared to only using the time since boot,
-        // which would also be OK behavior by itself.
-        // - If lastCrashTime is incorrectly more than the current time, only look at uptime
-        // - If it is much less than current time, only look at uptime
-        // - If current time is during the next few hours after last crash time, don't crash.
-        //   Considering that this only matters if last boot was some time ago, it's likely that
-        //   time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being
-        //   in this last state would also not last for long since the window is only a few hours.
-        final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit;
-        final boolean justBooted = uptime < minUptimeBeforeCrash;
-        final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now);
-        final boolean haveKnownRecentCrash =
-                haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs);
-        if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) {
-            // The system is not bound to its network stack (for example due to a crash in the
-            // network stack process): better crash rather than stay in a bad state where all
-            // networking is broken.
-            // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous
-            // API to persist settings before a crash.
-            tryWriteLastCrashTime(prefs, now);
-            throw new IllegalStateException(message);
-        }
-
-        // Here the system crashed recently already. Inform listeners that something is
-        // definitely wrong.
-        if (packageName != null) {
-            final ArraySet<NetworkStackHealthListener> listeners;
-            synchronized (mHealthListeners) {
-                listeners = new ArraySet<>(mHealthListeners);
-            }
-            for (NetworkStackHealthListener listener : listeners) {
-                listener.onNetworkStackFailure(packageName);
-            }
-        }
-    }
-
-    @Nullable
-    private SharedPreferences getSharedPreferences(@NonNull Context context) {
-        try {
-            final File prefsFile = new File(
-                    Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE);
-            return context.createDeviceProtectedStorageContext()
-                    .getSharedPreferences(prefsFile, Context.MODE_PRIVATE);
-        } catch (Throwable e) {
-            logWtf("Error loading shared preferences", e);
-            return null;
-        }
-    }
-
-    private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) {
-        if (prefs == null) return 0L;
-        try {
-            return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L);
-        } catch (Throwable e) {
-            logWtf("Error getting last crash time", e);
-            return 0L;
-        }
-    }
-
-    private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) {
-        if (prefs == null) return;
-        try {
-            prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit();
-        } catch (Throwable e) {
-            logWtf("Error writing last crash time", e);
-        }
-    }
-
     /**
      * Log a message in the local log.
      */
@@ -524,6 +291,8 @@
     public void dump(PrintWriter pw) {
         // dump is thread-safe on SharedLog
         mLog.dump(null, pw, null);
+        // dump connectivity module connector logs.
+        ConnectivityModuleConnector.getInstance().dump(pw);
 
         final int requestsQueueLength;
         synchronized (mPendingNetStackRequests) {
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
similarity index 78%
rename from services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
rename to services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
index 0d99561..a1bfcdf 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -36,8 +36,6 @@
 import static org.robolectric.Shadows.shadowOf;
 import static org.testng.Assert.expectThrows;
 
-import static java.util.Collections.emptySet;
-
 import android.annotation.UserIdInt;
 import android.app.Application;
 import android.app.backup.IBackupManagerMonitor;
@@ -48,16 +46,19 @@
 import android.content.Intent;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.platform.test.annotations.Presubmit;
 import android.util.SparseArray;
 
 import com.android.server.backup.testing.TransportData;
 import com.android.server.testing.shadows.ShadowApplicationPackageManager;
 import com.android.server.testing.shadows.ShadowBinder;
+import com.android.server.testing.shadows.ShadowEnvironment;
 import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
+import com.android.server.testing.shadows.ShadowUserManager;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -66,6 +67,7 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowContextWrapper;
 
 import java.io.File;
@@ -74,28 +76,32 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
+/** Tests for {@link BackupManagerService}. */
 @RunWith(RobolectricTestRunner.class)
 @Config(
         shadows = {
-            ShadowApplicationPackageManager.class,
-            ShadowBinder.class,
-            ShadowSystemServiceRegistry.class
+                ShadowApplicationPackageManager.class,
+                ShadowBinder.class,
+                ShadowUserManager.class,
+                ShadowEnvironment.class,
+                ShadowSystemServiceRegistry.class
         })
 @Presubmit
-public class BackupManagerServiceTest {
+public class BackupManagerServiceRoboTest {
     private static final String TEST_PACKAGE = "package";
     private static final String TEST_TRANSPORT = "transport";
     private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
 
-    private ShadowContextWrapper mShadowContext;
     private Context mContext;
+    private ShadowContextWrapper mShadowContext;
+    private ShadowUserManager mShadowUserManager;
     @UserIdInt private int mUserOneId;
     @UserIdInt private int mUserTwoId;
+    @Mock private UserBackupManagerService mUserSystemService;
     @Mock private UserBackupManagerService mUserOneService;
     @Mock private UserBackupManagerService mUserTwoService;
 
-    /** Initialize {@link BackupManagerService}. */
+    /** Setup */
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -103,66 +109,26 @@
         Application application = RuntimeEnvironment.application;
         mContext = application;
         mShadowContext = shadowOf(application);
+        mShadowUserManager = Shadow.extract(UserManager.get(application));
 
         mUserOneId = UserHandle.USER_SYSTEM + 1;
         mUserTwoId = mUserOneId + 1;
-    }
+        mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0);
+        mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0);
 
-    /**
-     * Clean up and reset state that was created for testing {@link BackupManagerService}
-     * operations.
-     */
-    @After
-    public void tearDown() throws Exception {
-        ShadowBinder.reset();
-    }
+        mShadowContext.grantPermissions(BACKUP);
+        mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
 
-    /**
-     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
-     * specifically to prevent overloading the logs in production.
-     */
-    @Test
-    public void testMoreDebug_isFalse() throws Exception {
-        boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
-        assertThat(moreDebug).isFalse();
-    }
-
-    /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
-    @Test
-    public void testConstructor_doesNotRegisterUsers() throws Exception {
-        BackupManagerService backupManagerService = createService();
-
-        assertThat(backupManagerService.getUserServices().size()).isEqualTo(0);
-    }
-
-    /** Test that the constructor handles {@code null} parameters. */
-    @Test
-    public void testConstructor_withNullContext_throws() throws Exception {
-        expectThrows(
-                NullPointerException.class,
-                () ->
-                        new BackupManagerService(
-                                /* context */ null,
-                                new Trampoline(mContext)));
-    }
-
-    /** Test that the constructor handles {@code null} parameters. */
-    @Test
-    public void testConstructor_withNullTrampoline_throws() throws Exception {
-        expectThrows(
-                NullPointerException.class,
-                () ->
-                        new BackupManagerService(
-                                mContext, /* trampoline */ null));
+        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
     }
 
     /** Test that the service registers users. */
     @Test
     public void testStartServiceForUser_registersUser() throws Exception {
         BackupManagerService backupManagerService = createService();
+        backupManagerService.setBackupServiceActive(mUserOneId, true);
 
-        backupManagerService.startServiceForUser(mUserOneId, emptySet());
+        backupManagerService.startServiceForUser(mUserOneId);
 
         SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
         assertThat(serviceUsers.size()).isEqualTo(1);
@@ -173,6 +139,7 @@
     @Test
     public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
         BackupManagerService backupManagerService = createService();
+        backupManagerService.setBackupServiceActive(mUserOneId, true);
 
         backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
 
@@ -187,6 +154,7 @@
         BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(mUserOneId, mUserOneService);
         backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
 
         backupManagerService.stopServiceForUser(mUserOneId);
 
@@ -201,6 +169,7 @@
     public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
         BackupManagerService backupManagerService =
                 createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.setBackupServiceActive(mUserTwoId, true);
         backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
 
         backupManagerService.stopServiceForUser(mUserOneId);
@@ -213,6 +182,8 @@
     @Test
     public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
         BackupManagerService backupManagerService = createService();
+        backupManagerService.setBackupServiceActive(mUserOneId, true);
+        ShadowBinder.setCallingUid(Process.SYSTEM_UID);
 
         backupManagerService.stopServiceForUser(mUserOneId);
 
@@ -220,53 +191,6 @@
         assertThat(serviceUsers.size()).isEqualTo(0);
     }
 
-    /**
-     * Test that the backup services throws a {@link SecurityException} if the caller does not have
-     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
-     */
-    @Test
-    public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        expectThrows(
-                SecurityException.class,
-                () ->
-                        backupManagerService.getServiceForUserIfCallerHasPermission(
-                                mUserOneId, "test"));
-    }
-
-    /**
-     * Test that the backup services does not throw a {@link SecurityException} if the caller has
-     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
-     */
-    @Test
-    public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
-
-        assertEquals(
-                mUserOneService,
-                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
-    }
-
-    /**
-     * Test that the backup services does not throw a {@link SecurityException} if the caller does
-     * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
-     */
-    @Test
-    public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        assertEquals(
-                mUserOneService,
-                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
-    }
-
     // ---------------------------------------------
     // Backup agent tests
     // ---------------------------------------------
@@ -274,8 +198,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
@@ -286,8 +210,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
@@ -298,8 +222,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         IBinder agentBinder = mock(IBinder.class);
 
@@ -311,8 +235,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         IBinder agentBinder = mock(IBinder.class);
 
@@ -323,33 +247,9 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
-    public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE);
-
-        verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE);
-
-        verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
     public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
@@ -360,8 +260,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
@@ -376,8 +276,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] transports = {TEST_TRANSPORT};
 
@@ -389,8 +289,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         String[] transports = {TEST_TRANSPORT};
 
@@ -402,8 +302,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
@@ -414,8 +314,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
@@ -426,8 +326,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.getCurrentTransport(mUserOneId);
@@ -438,8 +338,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.getCurrentTransport(mUserTwoId);
@@ -451,8 +351,8 @@
     @Test
     public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.getCurrentTransportComponent(mUserOneId);
@@ -464,8 +364,8 @@
     @Test
     public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.getCurrentTransportComponent(mUserTwoId);
@@ -476,8 +376,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.listAllTransports(mUserOneId);
@@ -488,8 +388,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.listAllTransports(mUserTwoId);
@@ -501,8 +401,8 @@
     @Test
     public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.listAllTransportComponents(mUserOneId);
@@ -514,8 +414,8 @@
     @Test
     public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.listAllTransportComponents(mUserTwoId);
@@ -525,10 +425,163 @@
 
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
+    public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(
+                mUserOneId, transport.getTransportComponent(), callback);
+
+        verify(mUserOneService)
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+            throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+        TransportData transport = backupTransport();
+        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+        backupManagerService.selectBackupTransportAsync(
+                mUserTwoId, transport.getTransportComponent(), callback);
+
+        verify(mUserOneService, never())
+                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
+    public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
+
+        verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service does not route methods for non-registered users. */
+    @Test
+    public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
+
+        verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
+    }
+
+    /** Test that the backup service routes methods correctly to the user that requests it. */
+    @Test
     public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         TransportData transport = backupTransport();
         Intent configurationIntent = new Intent();
@@ -557,8 +610,8 @@
     @Test
     public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         TransportData transport = backupTransport();
         Intent configurationIntent = new Intent();
@@ -583,170 +636,18 @@
                         "dataManagementLabel");
     }
 
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
-
-        verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
-
-        verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-        TransportData transport = backupTransport();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(
-                mUserOneId, transport.getTransportComponent(), callback);
-
-        verify(mUserOneService)
-                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
-            throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-        TransportData transport = backupTransport();
-        ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
-        backupManagerService.selectBackupTransportAsync(
-                mUserTwoId, transport.getTransportComponent(), callback);
-
-        verify(mUserOneService, never())
-                .selectBackupTransportAsync(transport.getTransportComponent(), callback);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
-
-        verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
-
-        verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
-
-        verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
-
-        verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
-
-        verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
-
-        verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service routes methods correctly to the user that requests it. */
-    @Test
-    public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
-
-        verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
-    }
-
-    /** Test that the backup service does not route methods for non-registered users. */
-    @Test
-    public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
-        backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
-
-        verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
-    }
-
     // ---------------------------------------------
     // Settings tests
     // ---------------------------------------------
+
     /**
      * Test that the backup services throws a {@link SecurityException} if the caller does not have
      * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
      */
     @Test
     public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(
@@ -760,9 +661,10 @@
      */
     @Test
     public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
         backupManagerService.setBackupEnabled(mUserTwoId, true);
@@ -773,8 +675,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.setBackupEnabled(mUserOneId, true);
@@ -785,8 +687,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.setBackupEnabled(mUserTwoId, true);
@@ -797,8 +699,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.setAutoRestore(mUserOneId, true);
@@ -809,8 +711,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.setAutoRestore(mUserTwoId, true);
@@ -821,8 +723,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.isBackupEnabled(mUserOneId);
@@ -833,8 +735,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.isBackupEnabled(mUserTwoId);
@@ -849,8 +751,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
@@ -861,8 +763,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
@@ -874,8 +776,8 @@
     @Test
     public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
 
@@ -888,8 +790,8 @@
     @Test
     public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
 
@@ -904,8 +806,8 @@
      */
     @Test
     public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
@@ -917,9 +819,10 @@
      */
     @Test
     public void testBackupNow_withPermission_propagatesForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
         backupManagerService.backupNow(mUserTwoId);
@@ -930,8 +833,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.backupNow(mUserOneId);
@@ -942,8 +845,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.backupNow(mUserTwoId);
@@ -957,8 +860,8 @@
      */
     @Test
     public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
@@ -977,9 +880,10 @@
      */
     @Test
     public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
@@ -993,8 +897,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
@@ -1008,8 +912,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         String[] packages = {TEST_PACKAGE};
         IBackupObserver observer = mock(IBackupObserver.class);
         IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
@@ -1026,8 +930,8 @@
      */
     @Test
     public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
@@ -1039,9 +943,9 @@
      */
     @Test
     public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
         backupManagerService.cancelBackups(mUserTwoId);
@@ -1052,8 +956,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.cancelBackups(mUserOneId);
@@ -1064,8 +968,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.cancelBackups(mUserTwoId);
@@ -1076,8 +980,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
         FullBackupJob job = new FullBackupJob();
 
         backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
@@ -1099,8 +1003,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
 
         backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
 
@@ -1120,8 +1024,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
 
@@ -1133,8 +1037,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         String[] packages = {TEST_PACKAGE};
 
@@ -1150,8 +1054,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
@@ -1162,8 +1066,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
@@ -1174,8 +1078,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
@@ -1186,8 +1090,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
@@ -1199,8 +1103,8 @@
     @Test
     public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
@@ -1211,8 +1115,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
         backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
@@ -1227,8 +1131,9 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
+        ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
 
         backupManagerService.setBackupPassword("currentPassword", "newPassword");
 
@@ -1248,8 +1153,9 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
+        ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
 
         backupManagerService.hasBackupPassword();
 
@@ -1272,8 +1178,9 @@
      */
     @Test
     public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(
@@ -1299,9 +1206,10 @@
      */
     @Test
     public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
@@ -1335,8 +1243,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -1370,8 +1278,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -1408,8 +1316,9 @@
      */
     @Test
     public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
         expectThrows(
@@ -1422,9 +1331,9 @@
      */
     @Test
     public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
-        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        registerUser(backupManagerService, mUserTwoId, mUserTwoService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
 
@@ -1436,8 +1345,8 @@
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
 
@@ -1449,8 +1358,8 @@
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
 
@@ -1459,12 +1368,18 @@
         verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
     }
 
+    private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
+        File testFile = new File(mContext.getFilesDir(), "test");
+        testFile.createNewFile();
+        return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+    }
+
     /** Test that the backup service routes methods correctly to the user that requests it. */
     @Test
     public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
         IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
 
@@ -1489,8 +1404,8 @@
     @Test
     public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
             throws Exception {
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
         IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
 
@@ -1512,48 +1427,6 @@
     }
 
     // ---------------------------------------------
-    //  Lifecycle tests
-    // ---------------------------------------------
-
-
-    /** testOnStart_publishesService */
-    @Test
-    public void testOnStart_publishesService() {
-        Trampoline trampoline = mock(Trampoline.class);
-        BackupManagerService.Lifecycle lifecycle =
-                spy(new BackupManagerService.Lifecycle(mContext, trampoline));
-        doNothing().when(lifecycle).publishService(anyString(), any());
-
-        lifecycle.onStart();
-
-        verify(lifecycle).publishService(Context.BACKUP_SERVICE, trampoline);
-    }
-
-    /** testOnUnlockUser_forwards */
-    @Test
-    public void testOnUnlockUser_forwards() {
-        Trampoline trampoline = mock(Trampoline.class);
-        BackupManagerService.Lifecycle lifecycle =
-                new BackupManagerService.Lifecycle(mContext, trampoline);
-
-        lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);
-
-        verify(trampoline).onUnlockUser(UserHandle.USER_SYSTEM);
-    }
-
-    /** testOnStopUser_forwards */
-    @Test
-    public void testOnStopUser_forwards() {
-        Trampoline trampoline = mock(Trampoline.class);
-        BackupManagerService.Lifecycle lifecycle =
-                new BackupManagerService.Lifecycle(mContext, trampoline);
-
-        lifecycle.onStopUser(UserHandle.USER_SYSTEM);
-
-        verify(trampoline).onStopUser(UserHandle.USER_SYSTEM);
-    }
-
-    // ---------------------------------------------
     //  Service tests
     // ---------------------------------------------
 
@@ -1561,24 +1434,22 @@
     @Test
     public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
         grantDumpPermissions();
-
-        BackupManagerService backupManagerService =
-                createServiceAndRegisterUser(UserHandle.USER_SYSTEM, mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
         File testFile = createTestFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
         PrintWriter printWriter = new PrintWriter(testFile);
         String[] args = {"1", "2"};
+        ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
 
         backupManagerService.dump(fileDescriptor, printWriter, args);
 
-        verify(mUserOneService).dump(fileDescriptor, printWriter, args);
+        verify(mUserSystemService).dump(fileDescriptor, printWriter, args);
     }
 
     /** Test that the backup service does not route methods for non-registered users. */
     @Test
     public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
         grantDumpPermissions();
-
         BackupManagerService backupManagerService = createService();
         File testFile = createTestFile();
         FileDescriptor fileDescriptor = new FileDescriptor();
@@ -1594,9 +1465,8 @@
     @Test
     public void testDump_users_dumpsListOfRegisteredUsers() {
         grantDumpPermissions();
-
-        BackupManagerService backupManagerService = createServiceAndRegisterUser(mUserOneId,
-                mUserOneService);
+        BackupManagerService backupManagerService = createSystemRegisteredService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
         StringWriter out = new StringWriter();
         PrintWriter writer = new PrintWriter(out);
         String[] args = {"users"};
@@ -1605,30 +1475,162 @@
 
         writer.flush();
         assertEquals(
-                String.format("%s %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE,
-                        mUserOneId),
+                String.format("%s %d %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE,
+                        UserHandle.USER_SYSTEM, mUserOneId),
                 out.toString());
     }
 
-    private void grantDumpPermissions() {
-        mShadowContext.grantPermissions(DUMP);
-        mShadowContext.grantPermissions(PACKAGE_USAGE_STATS);
-    }
-
     private File createTestFile() throws IOException {
         File testFile = new File(mContext.getFilesDir(), "test");
         testFile.createNewFile();
         return testFile;
     }
 
+    private void grantDumpPermissions() {
+        mShadowContext.grantPermissions(DUMP);
+        mShadowContext.grantPermissions(PACKAGE_USAGE_STATS);
+    }
+
+    /**
+     * Test that the backup services throws a {@link SecurityException} if the caller does not have
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+        expectThrows(
+                SecurityException.class,
+                () ->
+                        backupManagerService.getServiceForUserIfCallerHasPermission(
+                                mUserOneId, "test"));
+    }
+
+    /**
+     * Test that the backup services does not throw a {@link SecurityException} if the caller has
+     * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+     */
+    @Test
+    public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+        assertEquals(
+                mUserOneService,
+                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+    }
+
+    /**
+     * Test that the backup services does not throw a {@link SecurityException} if the caller does
+     * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
+     */
+    @Test
+    public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, mUserOneId, mUserOneService);
+        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+        assertEquals(
+                mUserOneService,
+                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+    }
+
+    /**
+     * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+     * specifically to prevent overloading the logs in production.
+     */
+    @Test
+    public void testMoreDebug_isFalse() throws Exception {
+        boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+        assertThat(moreDebug).isFalse();
+    }
+
+    /** Test that the constructor handles {@code null} parameters. */
+    @Test
+    public void testConstructor_withNullContext_throws() throws Exception {
+        expectThrows(
+                NullPointerException.class,
+                () ->
+                        new BackupManagerService(
+                                /* context */ null,
+                                new SparseArray<>()));
+    }
+
+    /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+    @Test
+    public void testConstructor_doesNotRegisterUsers() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        assertThat(backupManagerService.getUserServices().size()).isEqualTo(0);
+    }
+
+    // ---------------------------------------------
+    //  Lifecycle tests
+    // ---------------------------------------------
+
+    /** testOnStart_publishesService */
+    @Test
+    public void testOnStart_publishesService() {
+        BackupManagerService backupManagerService = mock(BackupManagerService.class);
+        BackupManagerService.Lifecycle lifecycle =
+                spy(new BackupManagerService.Lifecycle(mContext, backupManagerService));
+        doNothing().when(lifecycle).publishService(anyString(), any());
+
+        lifecycle.onStart();
+
+        verify(lifecycle).publishService(Context.BACKUP_SERVICE, backupManagerService);
+    }
+
+    /** testOnUnlockUser_forwards */
+    @Test
+    public void testOnUnlockUser_forwards() {
+        BackupManagerService backupManagerService = mock(BackupManagerService.class);
+        BackupManagerService.Lifecycle lifecycle =
+                new BackupManagerService.Lifecycle(mContext, backupManagerService);
+
+        lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);
+
+        verify(backupManagerService).onUnlockUser(UserHandle.USER_SYSTEM);
+    }
+
+    /** testOnStopUser_forwards */
+    @Test
+    public void testOnStopUser_forwards() {
+        BackupManagerService backupManagerService = mock(BackupManagerService.class);
+        BackupManagerService.Lifecycle lifecycle =
+                new BackupManagerService.Lifecycle(mContext, backupManagerService);
+
+        lifecycle.onStopUser(UserHandle.USER_SYSTEM);
+
+        verify(backupManagerService).onStopUser(UserHandle.USER_SYSTEM);
+    }
+
     private BackupManagerService createService() {
-        mShadowContext.grantPermissions(BACKUP);
-        return new BackupManagerService(mContext, new Trampoline(mContext));
+        return new BackupManagerService(mContext);
+    }
+
+    private BackupManagerService createSystemRegisteredService() {
+        BackupManagerService backupManagerService = createService();
+        registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserSystemService);
+        return backupManagerService;
+    }
+
+    private void registerUser(
+            BackupManagerService backupManagerService,
+            int userId,
+            UserBackupManagerService userBackupManagerService) {
+        backupManagerService.setBackupServiceActive(userId, true);
+        backupManagerService.startServiceForUser(userId, userBackupManagerService);
     }
 
     private BackupManagerService createServiceAndRegisterUser(
             int userId, UserBackupManagerService userBackupManagerService) {
         BackupManagerService backupManagerService = createService();
+        backupManagerService.setBackupServiceActive(userBackupManagerService.getUserId(), true);
         backupManagerService.startServiceForUser(userId, userBackupManagerService);
         return backupManagerService;
     }
@@ -1647,10 +1649,4 @@
             mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
         }
     }
-
-    private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
-        File testFile = new File(mContext.getFilesDir(), "test");
-        testFile.createNewFile();
-        return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
-    }
 }
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 84e810d..8632ca4 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -1005,7 +1005,7 @@
         UserBackupManagerService.createAndInitializeService(
                 USER_ID,
                 mContext,
-                new Trampoline(mContext),
+                new BackupManagerService(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
@@ -1026,7 +1026,7 @@
         UserBackupManagerService.createAndInitializeService(
                 USER_ID,
                 mContext,
-                new Trampoline(mContext),
+                new BackupManagerService(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
@@ -1045,7 +1045,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 /* context */ null,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 mBaseStateDir,
                                 mDataDir,
@@ -1077,7 +1077,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 /* backupThread */ null,
                                 mBaseStateDir,
                                 mDataDir,
@@ -1093,7 +1093,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 /* baseStateDir */ null,
                                 mDataDir,
@@ -1102,8 +1102,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
-     * File, File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
+     * HandlerThread, File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1113,7 +1113,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 mBaseStateDir,
                                 /* dataDir */ null,
@@ -1122,8 +1122,8 @@
 
     /**
      * Test checking non-null argument on {@link
-     * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
-     * File, File, TransportManager)}.
+     * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
+     * HandlerThread, File, File, TransportManager)}.
      */
     @Test
     public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1133,7 +1133,7 @@
                         UserBackupManagerService.createAndInitializeService(
                                 USER_ID,
                                 mContext,
-                                new Trampoline(mContext),
+                                new BackupManagerService(mContext),
                                 mBackupThread,
                                 mBaseStateDir,
                                 mDataDir,
@@ -1151,7 +1151,7 @@
         UserBackupManagerService service = UserBackupManagerService.createAndInitializeService(
                 USER_ID,
                 contextSpy,
-                new Trampoline(mContext),
+                new BackupManagerService(mContext),
                 mBackupThread,
                 mBaseStateDir,
                 mDataDir,
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 392d182..84421ef 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,7 +37,7 @@
 import android.util.Log;
 
 import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.Trampoline;
+import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.UserBackupManagerService;
 
@@ -89,7 +89,7 @@
                 UserBackupManagerService.createAndInitializeService(
                         userId,
                         context,
-                        new Trampoline(context),
+                        new BackupManagerService(context),
                         backupThread,
                         baseStateDir,
                         dataDir,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowEnvironment.java b/services/robotests/src/com/android/server/testing/shadows/ShadowEnvironment.java
new file mode 100644
index 0000000..577b082
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowEnvironment.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 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.testing.shadows;
+
+import android.annotation.Nullable;
+import android.os.Environment;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+import java.io.File;
+import java.nio.file.Path;
+
+/** Implementation mimics {@link org.robolectric.shadows.ShadowEnvironment}. */
+@Implements(Environment.class)
+public class ShadowEnvironment extends org.robolectric.shadows.ShadowEnvironment {
+    @Nullable private static Path sDataDirectory;
+
+    /** @see Environment#getDataDirectory() */
+    @Implementation
+    public static File getDataDirectory() {
+        if (sDataDirectory == null) {
+            sDataDirectory = RuntimeEnvironment.getTempDirectory().create("data");
+        }
+        return sDataDirectory.toFile();
+    }
+
+    /** Resets static state. */
+    @Resetter
+    public static void reset() {
+        org.robolectric.shadows.ShadowEnvironment.reset();
+        sDataDirectory = null;
+    }
+}
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java b/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java
new file mode 100644
index 0000000..c6ae1a1
--- /dev/null
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowUserManager.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 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.testing.shadows;
+
+import android.annotation.UserIdInt;
+import android.os.UserManager;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Shadow for {@link UserManager}. */
+@Implements(UserManager.class)
+public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager {
+    /** @see UserManager#isUserUnlocked() */
+    @Implementation
+    public boolean isUserUnlocked(@UserIdInt int userId) {
+        return false;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
index 1e29ed6..6e8b86a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/AlarmManagerServiceTest.java
@@ -253,7 +253,7 @@
         doReturn(mIActivityManager).when(ActivityManager::getService);
         doReturn(mAppStateTracker).when(() -> LocalServices.getService(AppStateTracker.class));
         doReturn(null)
-                .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class));
+                .when(() -> LocalServices.getService(DeviceIdleInternal.class));
         doReturn(mUsageStatsManagerInternal).when(
                 () -> LocalServices.getService(UsageStatsManagerInternal.class));
         when(mUsageStatsManagerInternal.getAppStandbyBucket(eq(TEST_CALLING_PACKAGE),
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index 6feac52..108b017 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -186,7 +186,7 @@
 
         @Override
         ConstraintController getConstraintController(
-                Handler handler, DeviceIdleController.LocalService localService) {
+                Handler handler, DeviceIdleInternal localService) {
             return constraintController;
         }
 
@@ -291,7 +291,7 @@
         // DeviceIdleController adds these to LocalServices in the constructor, so we have to remove
         // them after each test, otherwise, subsequent tests will fail.
         LocalServices.removeServiceForTest(AppStateTracker.class);
-        LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class);
+        LocalServices.removeServiceForTest(DeviceIdleInternal.class);
     }
 
     @Test
diff --git a/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
index f74ac1f..a9d1c2d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/deviceidle/BluetoothConstraintTest.java
@@ -39,7 +39,7 @@
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -71,7 +71,7 @@
     private BluetoothManager mBluetoothManager;
 
     @Mock
-    private DeviceIdleController.LocalService mDeviceIdleService;
+    private DeviceIdleInternal mDeviceIdleService;
 
     private BluetoothConstraint mConstraint;
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 22cd3d3..71f7d2c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -53,7 +53,7 @@
 import android.os.SystemClock;
 
 import com.android.server.AppStateTracker;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 import com.android.server.job.controllers.JobStatus;
 
@@ -114,8 +114,8 @@
         when(mContext.getSystemService(NetworkPolicyManager.class))
                 .thenReturn(mock(NetworkPolicyManager.class));
         // Called in DeviceIdleJobsController constructor.
-        doReturn(mock(DeviceIdleController.LocalService.class))
-                .when(() -> LocalServices.getService(DeviceIdleController.LocalService.class));
+        doReturn(mock(DeviceIdleInternal.class))
+                .when(() -> LocalServices.getService(DeviceIdleInternal.class));
         // Used in JobStatus.
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 7887d5b..93c16fe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -77,6 +77,8 @@
     private static final boolean FORCE_SEND = true;
     private static final boolean SEND_ON_WINDOW_CHANGES = false;
     private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
+    private static final int USER_PROFILE = 11;
+    private static final int USER_PROFILE_PARENT = 1;
     // TO-DO [Multi-Display] : change the display count to 2
     private static final int DISPLAY_COUNT = 1;
     private static final int NUM_GLOBAL_WINDOWS = 4;
@@ -110,6 +112,8 @@
         MockitoAnnotations.initMocks(this);
         when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID);
         when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
+                USER_PROFILE)).thenReturn(USER_PROFILE_PARENT);
+        when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
                 USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID);
         when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked(
                 anyString(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
@@ -247,8 +251,8 @@
             throws RemoteException {
         final AccessibilityWindowInfo oldWindow =
                 mA11yWindowManager.getWindowListLocked().get(0);
-        final IWindow token =
-                addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, true);
+        final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+                true, USER_SYSTEM_ID);
         final WindowInfo windowInfo = WindowInfo.obtain();
         windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
         windowInfo.token = token.asBinder();
@@ -605,6 +609,16 @@
         verify(mockRemoteConnection).notifyOutsideTouch();
     }
 
+    @Test
+    public void addAccessibilityInteractionConnection_profileUser_findInParentUser()
+            throws RemoteException {
+        final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+                false, USER_PROFILE);
+        final int windowId = mA11yWindowManager.findWindowIdLocked(
+                USER_PROFILE_PARENT, token.asBinder());
+        assertTrue(windowId >= 0);
+    }
+
     private void startTrackingPerDisplay(int displayId) throws RemoteException {
         ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
         // Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
@@ -612,12 +626,14 @@
         // for the test.
         int layer = 0;
         for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
-            final IWindow token = addAccessibilityInteractionConnection(displayId, true);
+            final IWindow token = addAccessibilityInteractionConnection(displayId,
+                    true, USER_SYSTEM_ID);
             addWindowInfo(windowInfosForDisplay, token, layer++);
 
         }
         for (int i = 0; i < NUM_APP_WINDOWS; i++) {
-            final IWindow token = addAccessibilityInteractionConnection(displayId, false);
+            final IWindow token = addAccessibilityInteractionConnection(displayId,
+                    false, USER_SYSTEM_ID);
             addWindowInfo(windowInfosForDisplay, token, layer++);
         }
         // Setups default focus.
@@ -647,8 +663,8 @@
         return windowsForAccessibilityCallbacksCaptor.getValue();
     }
 
-    private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal)
-            throws RemoteException {
+    private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
+            int userId) throws RemoteException {
         final IWindow mockWindowToken = Mockito.mock(IWindow.class);
         final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
                 IAccessibilityInteractionConnection.class);
@@ -656,13 +672,13 @@
         final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
         when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
         when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
-        when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(USER_SYSTEM_ID))
+        when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
                 .thenReturn(bGlobal);
         when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
                 .thenReturn(displayId);
 
         int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
-                mockWindowToken, mockA11yConnection, PACKAGE_NAME, USER_SYSTEM_ID);
+                mockWindowToken, mockA11yConnection, PACKAGE_NAME, userId);
         mA11yWindowTokens.put(windowId, mockWindowToken);
         return mockWindowToken;
     }
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 0000000..68b413f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2017 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.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.annotation.UserIdInt;
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.utils.RandomAccessFileUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupManagerServiceTest {
+    private static final String PACKAGE_NAME = "some.package.name";
+    private static final String TRANSPORT_NAME = "some.transport.name";
+    private static final String CURRENT_PASSWORD = "current_password";
+    private static final String NEW_PASSWORD = "new_password";
+    private static final String ENCRYPTION_PASSWORD = "encryption_password";
+    private static final CharSequence DATA_MANAGEMENT_LABEL = "data_management_label";
+    private static final String DESTINATION_STRING = "destination_string";
+    private static final String[] PACKAGE_NAMES =
+            new String[]{"some.package.name._1", "some.package.name._2"};
+    private static final String[] TRANSPORTS =
+            new String[]{"some.transport.name._1", "some.transport.name._2"};
+    private static final ComponentName TRANSPORT_COMPONENT_NAME = new ComponentName("package",
+            "class");
+    private static final ComponentName[] TRANSPORT_COMPONENTS = new ComponentName[]{
+            new ComponentName("package1", "class1"),
+            new ComponentName("package2", "class2")
+    };
+    private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+    private static final int UNSTARTED_NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 2;
+
+    @UserIdInt
+    private int mUserId;
+    @Mock
+    private UserBackupManagerService mUserBackupManagerService;
+    @Mock
+    private Context mContextMock;
+    @Mock
+    private IBinder mAgentMock;
+    @Mock
+    private ParcelFileDescriptor mParcelFileDescriptorMock;
+    @Mock
+    private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
+    @Mock
+    private IBackupObserver mBackupObserverMock;
+    @Mock
+    private IBackupManagerMonitor mBackupManagerMonitorMock;
+    @Mock
+    private PrintWriter mPrintWriterMock;
+    @Mock
+    private UserManager mUserManagerMock;
+    @Mock
+    private UserInfo mUserInfoMock;
+
+    private FileDescriptor mFileDescriptorStub = new FileDescriptor();
+
+    private BackupManagerServiceTestable mService;
+    private File mTestDir;
+    private File mSuppressFile;
+    private SparseArray<UserBackupManagerService> mUserServices;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mUserId = UserHandle.USER_SYSTEM;
+
+        mUserServices = new SparseArray<>();
+        mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
+        mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService);
+
+        when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
+        when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+        when(mUserManagerMock.getUserInfo(UNSTARTED_NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+
+        BackupManagerServiceTestable.sCallingUserId = UserHandle.USER_SYSTEM;
+        BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerServiceTestable.sUserManagerMock = mUserManagerMock;
+
+        mTestDir = InstrumentationRegistry.getContext().getFilesDir();
+        mTestDir.mkdirs();
+
+        mSuppressFile = new File(mTestDir, "suppress");
+        BackupManagerServiceTestable.sSuppressFile = mSuppressFile;
+
+        setUpStateFilesForNonSystemUser(NON_USER_SYSTEM);
+        setUpStateFilesForNonSystemUser(UNSTARTED_NON_USER_SYSTEM);
+
+        when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE))
+                .thenReturn(mock(JobScheduler.class));
+        mService = new BackupManagerServiceTestable(mContextMock, mUserServices);
+    }
+
+    private void setUpStateFilesForNonSystemUser(int userId) {
+        File activatedFile = new File(mTestDir, "activate-" + userId);
+        BackupManagerServiceTestable.sActivatedFiles.append(userId, activatedFile);
+        File rememberActivatedFile = new File(mTestDir, "rem-activate-" + userId);
+        BackupManagerServiceTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mSuppressFile.delete();
+        deleteFiles(BackupManagerServiceTestable.sActivatedFiles);
+        deleteFiles(BackupManagerServiceTestable.sRememberActivatedFiles);
+    }
+
+    private void deleteFiles(SparseArray<File> files) {
+        int numFiles = files.size();
+        for (int i = 0; i < numFiles; i++) {
+            files.valueAt(i).delete();
+        }
+    }
+
+    @Test
+    public void testIsBackupServiceActive_whenBackupsNotDisabledAndSuppressFileDoesNotExist() {
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void testOnUnlockUser_forNonSystemUserWhenBackupsDisabled_doesNotStartUser() {
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+        ConditionVariable unlocked = new ConditionVariable(false);
+
+        service.onUnlockUser(NON_USER_SYSTEM);
+
+        service.getBackupHandler().post(unlocked::open);
+        unlocked.block();
+        assertNull(service.getUserService(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void testOnUnlockUser_forSystemUserWhenBackupsDisabled_doesNotStartUser() {
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+        ConditionVariable unlocked = new ConditionVariable(false);
+
+        service.onUnlockUser(UserHandle.USER_SYSTEM);
+
+        service.getBackupHandler().post(unlocked::open);
+        unlocked.block();
+        assertNull(service.getUserService(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void testOnUnlockUser_whenBackupNotActivated_doesNotStartUser() {
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+        service.setBackupServiceActive(NON_USER_SYSTEM, false);
+        ConditionVariable unlocked = new ConditionVariable(false);
+
+        service.onUnlockUser(NON_USER_SYSTEM);
+
+        service.getBackupHandler().post(unlocked::open);
+        unlocked.block();
+        assertNull(service.getUserService(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void testIsBackupServiceActive_forSystemUserWhenBackupDisabled_returnsTrue()
+            throws Exception {
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        backupManagerService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        assertFalse(backupManagerService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void testIsBackupServiceActive_forNonSystemUserWhenBackupDisabled_returnsTrue()
+            throws Exception {
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        backupManagerService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertFalse(backupManagerService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+        assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
+            throws Exception {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
+            throws Exception {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        // Don't activate non-system user.
+
+        assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void
+            isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
+            throws Exception {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void
+            isBackupServiceActive_forUnstartedNonSystemUser_returnsTrueWhenSystemAndUserActivated()
+            throws Exception {
+        mService.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
+        BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
+        BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
+
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
+        BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+
+        try {
+            mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+            fail();
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
+        when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+        BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
+        when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+        BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
+
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
+        when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+        BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+
+        try {
+            mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+            fail();
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
+        doThrow(new SecurityException())
+                .when(mContextMock)
+                .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
+
+        try {
+            mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+            fail();
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
+        doThrow(new SecurityException())
+                .when(mContextMock)
+                .enforceCallingOrSelfPermission(
+                        eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
+
+        try {
+            mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+            fail();
+        } catch (SecurityException expected) {
+        }
+    }
+
+    @Test
+    public void setBackupServiceActive_backupDisabled_ignored() {
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerServiceTestable service =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+
+        service.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        assertFalse(service.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_alreadyActive_ignored() {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+        assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
+            throws IOException {
+        assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+        assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() {
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
+    }
+
+    @Test
+    public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
+        int otherUser = NON_USER_SYSTEM + 1;
+        File activateFile = new File(mTestDir, "activate-" + otherUser);
+        BackupManagerServiceTestable.sActivatedFiles.append(otherUser, activateFile);
+        mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+        assertFalse(mService.isBackupServiceActive(otherUser));
+        activateFile.delete();
+    }
+
+    @Test
+    public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
+
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+        assertTrue(RandomAccessFileUtils.readBoolean(
+                BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false));
+    }
+
+    @Test
+    public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
+
+        mService.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+        assertFalse(RandomAccessFileUtils.readBoolean(
+                BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
+    }
+
+    @Test
+    public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
+        mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+        mService.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+        assertFalse(RandomAccessFileUtils.readBoolean(
+                BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
+    }
+
+    @Test
+    public void selectBackupTransportAsyncForUser_beforeUserUnlocked_notifiesBackupNotAllowed()
+            throws Exception {
+        mUserServices.clear();
+        CompletableFuture<Integer> future = new CompletableFuture<>();
+        ISelectBackupTransportCallback listener =
+                new ISelectBackupTransportCallback.Stub() {
+                    @Override
+                    public void onSuccess(String transportName) {
+                        future.completeExceptionally(new AssertionError());
+                    }
+                    @Override
+                    public void onFailure(int reason) {
+                        future.complete(reason);
+                    }
+                };
+
+        mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+
+        assertEquals(BackupManager.ERROR_BACKUP_NOT_ALLOWED, (int) future.get(5, TimeUnit.SECONDS));
+    }
+
+    @Test
+    public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
+            throws Exception {
+        mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
+        // No crash.
+    }
+
+    @Test
+    public void
+            selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow()
+            throws Exception {
+        ISelectBackupTransportCallback.Stub listener =
+                new ISelectBackupTransportCallback.Stub() {
+                    @Override
+                    public void onSuccess(String transportName) {}
+                    @Override
+                    public void onFailure(int reason) throws RemoteException {
+                        throw new RemoteException();
+                    }
+                };
+
+        mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+
+        // No crash.
+    }
+
+    @Test
+    public void dump_callerDoesNotHavePermission_ignored() {
+        when(mContextMock.checkCallingOrSelfPermission(
+                android.Manifest.permission.DUMP)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+
+        mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
+
+        verifyNoMoreInteractions(mUserBackupManagerService);
+    }
+
+    public void testGetUserForAncestralSerialNumber() {
+        BackupManagerServiceTestable.sBackupDisabled = false;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
+
+        UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
+
+        assertThat(user).isEqualTo(UserHandle.of(1));
+    }
+
+    public void testGetUserForAncestralSerialNumber_whenDisabled() {
+        BackupManagerServiceTestable.sBackupDisabled = true;
+        BackupManagerService backupManagerService =
+                new BackupManagerServiceTestable(mContextMock, mUserServices);
+        when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
+
+        UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
+
+        assertThat(user).isNull();
+    }
+
+    private static class BackupManagerServiceTestable extends BackupManagerService {
+        static boolean sBackupDisabled = false;
+        static int sCallingUserId = -1;
+        static int sCallingUid = -1;
+        static File sSuppressFile = null;
+        static SparseArray<File> sActivatedFiles = new SparseArray<>();
+        static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
+        static UserManager sUserManagerMock = null;
+
+        BackupManagerServiceTestable(
+                Context context, SparseArray<UserBackupManagerService> userServices) {
+            super(context, userServices);
+        }
+
+        @Override
+        protected UserManager getUserManager() {
+            return sUserManagerMock;
+        }
+
+        @Override
+        protected boolean isBackupDisabled() {
+            return sBackupDisabled;
+        }
+
+        @Override
+        protected File getSuppressFileForSystemUser() {
+            return sSuppressFile;
+        }
+
+        @Override
+        protected File getRememberActivatedFileForNonSystemUser(int userId) {
+            return sRememberActivatedFiles.get(userId);
+        }
+
+        @Override
+        protected File getActivatedFileForNonSystemUser(int userId) {
+            return sActivatedFiles.get(userId);
+        }
+
+        protected int binderGetCallingUserId() {
+            return sCallingUserId;
+        }
+
+        @Override
+        protected int binderGetCallingUid() {
+            return sCallingUid;
+        }
+
+        @Override
+        protected void postToHandler(Runnable runnable) {
+            runnable.run();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
deleted file mode 100644
index 8668a3c..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ /dev/null
@@ -1,1116 +0,0 @@
-/*
- * Copyright (C) 2017 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.backup;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.annotation.UserIdInt;
-import android.app.backup.BackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.backup.utils.RandomAccessFileUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class TrampolineTest {
-    private static final String PACKAGE_NAME = "some.package.name";
-    private static final String TRANSPORT_NAME = "some.transport.name";
-    private static final String CURRENT_PASSWORD = "current_password";
-    private static final String NEW_PASSWORD = "new_password";
-    private static final String ENCRYPTION_PASSWORD = "encryption_password";
-    private static final CharSequence DATA_MANAGEMENT_LABEL = "data_management_label";
-    private static final String DESTINATION_STRING = "destination_string";
-    private static final String[] PACKAGE_NAMES =
-            new String[]{"some.package.name._1", "some.package.name._2"};
-    private static final String[] TRANSPORTS =
-            new String[]{"some.transport.name._1", "some.transport.name._2"};
-    private static final ComponentName TRANSPORT_COMPONENT_NAME = new ComponentName("package",
-            "class");
-    private static final ComponentName[] TRANSPORT_COMPONENTS = new ComponentName[]{
-            new ComponentName("package1", "class1"),
-            new ComponentName("package2", "class2")
-    };
-    private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
-    private static final int UNSTARTED_NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 2;
-
-    @UserIdInt
-    private int mUserId;
-    @Mock
-    private BackupManagerService mBackupManagerServiceMock;
-    @Mock
-    private UserBackupManagerService mUserBackupManagerService;
-    @Mock
-    private Context mContextMock;
-    @Mock
-    private IBinder mAgentMock;
-    @Mock
-    private ParcelFileDescriptor mParcelFileDescriptorMock;
-    @Mock
-    private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
-    @Mock
-    private IBackupObserver mBackupObserverMock;
-    @Mock
-    private IBackupManagerMonitor mBackupManagerMonitorMock;
-    @Mock
-    private PrintWriter mPrintWriterMock;
-    @Mock
-    private UserManager mUserManagerMock;
-    @Mock
-    private UserInfo mUserInfoMock;
-
-    private FileDescriptor mFileDescriptorStub = new FileDescriptor();
-
-    private TrampolineTestable mTrampoline;
-    private File mTestDir;
-    private File mSuppressFile;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        mUserId = UserHandle.USER_SYSTEM;
-
-        SparseArray<UserBackupManagerService> serviceUsers = new SparseArray<>();
-        serviceUsers.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
-        serviceUsers.append(NON_USER_SYSTEM, mUserBackupManagerService);
-        when(mBackupManagerServiceMock.getUserServices()).thenReturn(serviceUsers);
-
-        when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
-        when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
-        when(mUserManagerMock.getUserInfo(UNSTARTED_NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
-
-        TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
-        TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
-        TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
-        TrampolineTestable.sBackupDisabled = false;
-        TrampolineTestable.sUserManagerMock = mUserManagerMock;
-
-        mTestDir = InstrumentationRegistry.getContext().getFilesDir();
-        mTestDir.mkdirs();
-
-        mSuppressFile = new File(mTestDir, "suppress");
-        TrampolineTestable.sSuppressFile = mSuppressFile;
-
-        setUpStateFilesForNonSystemUser(NON_USER_SYSTEM);
-        setUpStateFilesForNonSystemUser(UNSTARTED_NON_USER_SYSTEM);
-
-        mTrampoline = new TrampolineTestable(mContextMock);
-    }
-
-    private void setUpStateFilesForNonSystemUser(int userId) {
-        File activatedFile = new File(mTestDir, "activate-" + userId);
-        TrampolineTestable.sActivatedFiles.append(userId, activatedFile);
-        File rememberActivatedFile = new File(mTestDir, "rem-activate-" + userId);
-        TrampolineTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        mSuppressFile.delete();
-        deleteFiles(TrampolineTestable.sActivatedFiles);
-        deleteFiles(TrampolineTestable.sRememberActivatedFiles);
-    }
-
-    private void deleteFiles(SparseArray<File> files) {
-        int numFiles = files.size();
-        for (int i = 0; i < numFiles; i++) {
-            files.valueAt(i).delete();
-        }
-    }
-
-    @Test
-    public void testIsBackupServiceActive_whenBackupsNotDisabledAndSuppressFileDoesNotExist() {
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void testOnUnlockUser_forNonSystemUserWhenBackupsDisabled_doesNotStartUser() {
-        when(mBackupManagerServiceMock.getUserServices()).thenReturn(new SparseArray<>());
-        TrampolineTestable.sBackupDisabled = true;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        ConditionVariable unlocked = new ConditionVariable(false);
-
-        trampoline.onUnlockUser(NON_USER_SYSTEM);
-
-        trampoline.getBackupHandler().post(unlocked::open);
-        unlocked.block();
-        assertNull(trampoline.getUserService(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void testOnUnlockUser_forSystemUserWhenBackupsDisabled_doesNotStartUser() {
-        when(mBackupManagerServiceMock.getUserServices()).thenReturn(new SparseArray<>());
-        TrampolineTestable.sBackupDisabled = true;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        ConditionVariable unlocked = new ConditionVariable(false);
-
-        trampoline.onUnlockUser(UserHandle.USER_SYSTEM);
-
-        trampoline.getBackupHandler().post(unlocked::open);
-        unlocked.block();
-        assertNull(trampoline.getUserService(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void testOnUnlockUser_whenBackupNotActivated_doesNotStartUser() {
-        when(mBackupManagerServiceMock.getUserServices()).thenReturn(new SparseArray<>());
-        TrampolineTestable.sBackupDisabled = false;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-        trampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
-        ConditionVariable unlocked = new ConditionVariable(false);
-
-        trampoline.onUnlockUser(NON_USER_SYSTEM);
-
-        trampoline.getBackupHandler().post(unlocked::open);
-        unlocked.block();
-        assertNull(trampoline.getUserService(NON_USER_SYSTEM));
-        //noinspection unchecked
-        verify(mBackupManagerServiceMock, never()).startServiceForUser(
-                eq(NON_USER_SYSTEM), any(Set.class));
-    }
-
-    @Test
-    public void testIsBackupServiceActive_forSystemUserWhenBackupDisabled_returnsTrue()
-            throws Exception {
-        TrampolineTestable.sBackupDisabled = true;
-        Trampoline trampoline = new TrampolineTestable(mContextMock);
-        trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void testIsBackupServiceActive_forNonSystemUserWhenBackupDisabled_returnsTrue()
-            throws Exception {
-        TrampolineTestable.sBackupDisabled = true;
-        Trampoline trampoline = new TrampolineTestable(mContextMock);
-        trampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertFalse(trampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
-            throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
-            throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        // Don't activate non-system user.
-
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void
-            isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
-                throws Exception {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void
-            isBackupServiceActive_forUnstartedNonSystemUser_returnsTrueWhenSystemAndUserActivated()
-            throws Exception {
-        mTrampoline.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
-        TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
-
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
-        TrampolineTestable.sCallingUid = Process.ROOT_UID;
-
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
-        TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
-
-        try {
-            mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-            fail();
-        } catch (SecurityException expected) {
-        }
-    }
-
-    @Test
-    public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
-        when(mUserInfoMock.isManagedProfile()).thenReturn(true);
-        TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
-
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
-        when(mUserInfoMock.isManagedProfile()).thenReturn(true);
-        TrampolineTestable.sCallingUid = Process.ROOT_UID;
-
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
-        when(mUserInfoMock.isManagedProfile()).thenReturn(true);
-        TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
-
-        try {
-            mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-            fail();
-        } catch (SecurityException expected) {
-        }
-    }
-
-    @Test
-    public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
-        doThrow(new SecurityException())
-                .when(mContextMock)
-                .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
-
-        try {
-            mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-            fail();
-        } catch (SecurityException expected) {
-        }
-    }
-
-    @Test
-    public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
-        doThrow(new SecurityException())
-                .when(mContextMock)
-                .enforceCallingOrSelfPermission(
-                        eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
-
-        try {
-            mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-            fail();
-        } catch (SecurityException expected) {
-        }
-    }
-
-    @Test
-    public void setBackupServiceActive_backupDisabled_ignored() {
-        TrampolineTestable.sBackupDisabled = true;
-        TrampolineTestable trampoline = new TrampolineTestable(mContextMock);
-
-        trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_alreadyActive_ignored() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
-            throws IOException {
-        assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
-        assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() {
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-    }
-
-    @Test
-    public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
-        int otherUser = NON_USER_SYSTEM + 1;
-        File activateFile = new File(mTestDir, "activate-" + otherUser);
-        TrampolineTestable.sActivatedFiles.append(otherUser, activateFile);
-        mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
-        assertFalse(mTrampoline.isBackupServiceActive(otherUser));
-        activateFile.delete();
-    }
-
-    @Test
-    public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
-
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
-        assertTrue(RandomAccessFileUtils.readBoolean(
-                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false));
-    }
-
-    @Test
-    public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
-
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
-
-        assertFalse(RandomAccessFileUtils.readBoolean(
-                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
-    }
-
-    @Test
-    public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-        mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
-
-        assertFalse(RandomAccessFileUtils.readBoolean(
-                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
-    }
-
-    @Test
-    public void dataChangedForUser_forwarded() throws Exception {
-        mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);
-
-        verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
-    }
-
-    @Test
-    public void dataChanged_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.dataChanged(PACKAGE_NAME);
-
-        verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
-    }
-
-    @Test
-    public void clearBackupDataForUser_forwarded() throws Exception {
-
-        mTrampoline.clearBackupDataForUser(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
-
-        verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
-    }
-
-    @Test
-    public void clearBackupData_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.clearBackupData(TRANSPORT_NAME, PACKAGE_NAME);
-
-        verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
-    }
-
-    @Test
-    public void agentConnectedForUser_forwarded() throws Exception {
-
-        mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);
-
-        verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
-    }
-
-    @Test
-    public void agentConnected_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);
-
-        verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
-    }
-
-    @Test
-    public void agentDisconnectedForUser_forwarded() throws Exception {
-
-        mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);
-
-        verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
-    }
-
-    @Test
-    public void agentDisconnected_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.agentDisconnected(PACKAGE_NAME);
-
-        verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
-    }
-
-    @Test
-    public void restoreAtInstallForUser_forwarded() throws Exception {
-
-        mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
-
-        verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
-    }
-
-    @Test
-    public void restoreAtInstall_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
-
-        verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
-    }
-
-    @Test
-    public void setBackupEnabledForUser_forwarded() throws Exception {
-
-        mTrampoline.setBackupEnabledForUser(mUserId, true);
-
-        verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
-    }
-
-    @Test
-    public void setBackupEnabled_forwardedToCallingUserId() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.setBackupEnabled(true);
-
-        verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
-    }
-
-    @Test
-    public void setAutoRestoreForUser_forwarded() throws Exception {
-
-        mTrampoline.setAutoRestoreForUser(mUserId, true);
-
-        verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
-    }
-
-    @Test
-    public void setAutoRestore_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.setAutoRestore(true);
-
-        verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
-    }
-
-    @Test
-    public void isBackupEnabledForUser_forwarded() throws Exception {
-
-        mTrampoline.isBackupEnabledForUser(mUserId);
-
-        verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
-    }
-
-    @Test
-    public void isBackupEnabled_forwardedToCallingUserId() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.isBackupEnabled();
-
-        verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
-    }
-
-    @Test
-    public void setBackupPassword_forwarded() throws Exception {
-        mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
-        verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
-    }
-
-    @Test
-    public void hasBackupPassword_forwarded() throws Exception {
-        mTrampoline.hasBackupPassword();
-        verify(mBackupManagerServiceMock).hasBackupPassword();
-    }
-
-    @Test
-    public void backupNowForUser_forwarded() throws Exception {
-
-        mTrampoline.backupNowForUser(mUserId);
-
-        verify(mBackupManagerServiceMock).backupNow(mUserId);
-    }
-
-    @Test
-    public void backupNow_forwardedToCallingUserId() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.backupNow();
-
-        verify(mBackupManagerServiceMock).backupNow(mUserId);
-    }
-
-    @Test
-    public void adbBackup_forwarded() throws Exception {
-        mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
-                true, true, true, true, true, true,
-                PACKAGE_NAMES);
-        verify(mBackupManagerServiceMock).adbBackup(mUserId, mParcelFileDescriptorMock, true,
-                true, true, true, true, true, true, true, PACKAGE_NAMES);
-    }
-
-    @Test
-    public void fullTransportBackupForUser_forwarded() throws Exception {
-
-        mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
-
-        verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES);
-    }
-
-    @Test
-    public void adbRestore_forwarded() throws Exception {
-        mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
-        verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
-    }
-
-    @Test
-    public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws Exception {
-
-        mTrampoline.acknowledgeFullBackupOrRestoreForUser(
-                mUserId,
-                123,
-                true,
-                CURRENT_PASSWORD,
-                ENCRYPTION_PASSWORD,
-                mFullBackupRestoreObserverMock);
-
-        verify(mBackupManagerServiceMock)
-                .acknowledgeAdbBackupOrRestore(
-                        mUserId,
-                        123,
-                        true,
-                        CURRENT_PASSWORD,
-                        ENCRYPTION_PASSWORD,
-                        mFullBackupRestoreObserverMock);
-    }
-
-    @Test
-    public void acknowledgeFullBackupOrRestore_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
-                mFullBackupRestoreObserverMock);
-
-        verify(mBackupManagerServiceMock)
-                .acknowledgeAdbBackupOrRestore(
-                        mUserId,
-                        123,
-                        true,
-                        CURRENT_PASSWORD,
-                        ENCRYPTION_PASSWORD,
-                        mFullBackupRestoreObserverMock);
-    }
-
-    @Test
-    public void getCurrentTransportForUser_forwarded() throws Exception {
-        when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
-
-        assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransportForUser(mUserId));
-        verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
-    }
-
-    @Test
-    public void getCurrentTransport_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-        when(mBackupManagerServiceMock.getCurrentTransport(mUserId)).thenReturn(TRANSPORT_NAME);
-
-        assertEquals(TRANSPORT_NAME, mTrampoline.getCurrentTransport());
-        verify(mBackupManagerServiceMock).getCurrentTransport(mUserId);
-    }
-
-    @Test
-    public void listAllTransportsForUser_forwarded() throws Exception {
-        when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
-
-        assertEquals(TRANSPORTS, mTrampoline.listAllTransportsForUser(mUserId));
-        verify(mBackupManagerServiceMock).listAllTransports(mUserId);
-    }
-
-
-    @Test
-    public void listAllTransports_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-        when(mBackupManagerServiceMock.listAllTransports(mUserId)).thenReturn(TRANSPORTS);
-
-        assertEquals(TRANSPORTS, mTrampoline.listAllTransports());
-        verify(mBackupManagerServiceMock).listAllTransports(mUserId);
-    }
-
-    @Test
-    public void listAllTransportComponentsForUser_forwarded() throws Exception {
-        when(mBackupManagerServiceMock.listAllTransportComponents(mUserId)).thenReturn(
-                TRANSPORT_COMPONENTS);
-
-        assertEquals(TRANSPORT_COMPONENTS, mTrampoline.listAllTransportComponentsForUser(mUserId));
-        verify(mBackupManagerServiceMock).listAllTransportComponents(mUserId);
-    }
-
-    @Test
-    public void updateTransportAttributesForUser_forwarded() {
-        mTrampoline.updateTransportAttributesForUser(
-                mUserId,
-                TRANSPORT_COMPONENT_NAME,
-                TRANSPORT_NAME,
-                null,
-                "Transport Destination",
-                null,
-                "Data Management");
-
-        verify(mBackupManagerServiceMock)
-                .updateTransportAttributes(
-                        mUserId,
-                        TRANSPORT_COMPONENT_NAME,
-                        TRANSPORT_NAME,
-                        null,
-                        "Transport Destination",
-                        null,
-                        "Data Management");
-    }
-
-    @Test
-    public void selectBackupTransportForUser_forwarded() throws Exception {
-
-        mTrampoline.selectBackupTransportForUser(mUserId, TRANSPORT_NAME);
-
-        verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void selectBackupTransport_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.selectBackupTransport(TRANSPORT_NAME);
-
-        verify(mBackupManagerServiceMock).selectBackupTransport(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void selectBackupTransportAsyncForUser_beforeUserUnlocked_notifiesBackupNotAllowed()
-            throws Exception {
-        when(mBackupManagerServiceMock.getUserServices()).thenReturn(new SparseArray<>());
-        CompletableFuture<Integer> future = new CompletableFuture<>();
-        ISelectBackupTransportCallback listener =
-                new ISelectBackupTransportCallback.Stub() {
-                    @Override
-                    public void onSuccess(String transportName) {
-                        future.completeExceptionally(new AssertionError());
-                    }
-                    @Override
-                    public void onFailure(int reason) {
-                        future.complete(reason);
-                    }
-                };
-
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
-
-        assertEquals(BackupManager.ERROR_BACKUP_NOT_ALLOWED, (int) future.get(5, TimeUnit.SECONDS));
-    }
-
-    @Test
-    public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
-            throws Exception {
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
-
-        // No crash.
-    }
-
-    @Test
-    public void
-            selectBackupTransportAsyncForUser_beforeUserUnlockedWithThrowingListener_doesNotThrow()
-            throws Exception {
-        ISelectBackupTransportCallback.Stub listener =
-                new ISelectBackupTransportCallback.Stub() {
-                    @Override
-                    public void onSuccess(String transportName) {}
-                    @Override
-                    public void onFailure(int reason) throws RemoteException {
-                        throw new RemoteException();
-                    }
-                };
-
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
-
-        // No crash.
-    }
-
-    @Test
-    public void selectBackupTransportAsyncForUser_forwarded() throws Exception {
-
-        mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
-
-        verify(mBackupManagerServiceMock)
-                .selectBackupTransportAsync(mUserId, TRANSPORT_COMPONENT_NAME, null);
-    }
-
-    @Test
-    public void getConfigurationIntentForUser_forwarded() throws Exception {
-        Intent configurationIntentStub = new Intent();
-        when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
-                configurationIntentStub);
-
-        assertEquals(
-                configurationIntentStub,
-                mTrampoline.getConfigurationIntentForUser(mUserId, TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void getConfigurationIntent_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-        Intent configurationIntentStub = new Intent();
-        when(mBackupManagerServiceMock.getConfigurationIntent(mUserId, TRANSPORT_NAME)).thenReturn(
-                configurationIntentStub);
-
-        assertEquals(configurationIntentStub, mTrampoline.getConfigurationIntent(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getConfigurationIntent(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void getDestinationStringForUser_forwarded() throws Exception {
-        when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
-                DESTINATION_STRING);
-
-        assertEquals(
-                DESTINATION_STRING,
-                mTrampoline.getDestinationStringForUser(mUserId, TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void getDestinationString_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-        when(mBackupManagerServiceMock.getDestinationString(mUserId, TRANSPORT_NAME)).thenReturn(
-                DESTINATION_STRING);
-
-        assertEquals(DESTINATION_STRING, mTrampoline.getDestinationString(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDestinationString(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void getDataManagementIntentForUser_forwarded() throws Exception {
-        Intent dataManagementIntent = new Intent();
-        when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
-                dataManagementIntent);
-
-        assertEquals(
-                dataManagementIntent,
-                mTrampoline.getDataManagementIntentForUser(mUserId, TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void getDataManagementIntent_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-        Intent dataManagementIntent = new Intent();
-        when(mBackupManagerServiceMock.getDataManagementIntent(mUserId, TRANSPORT_NAME)).thenReturn(
-                dataManagementIntent);
-
-        assertEquals(dataManagementIntent, mTrampoline.getDataManagementIntent(TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDataManagementIntent(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void getDataManagementLabelForUser_forwarded() throws Exception {
-        when(mBackupManagerServiceMock.getDataManagementLabel(mUserId, TRANSPORT_NAME)).thenReturn(
-                DATA_MANAGEMENT_LABEL);
-
-        assertEquals(
-                DATA_MANAGEMENT_LABEL,
-                mTrampoline.getDataManagementLabelForUser(mUserId, TRANSPORT_NAME));
-        verify(mBackupManagerServiceMock).getDataManagementLabel(mUserId, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void beginRestoreSessionForUser_forwarded() throws Exception {
-
-        mTrampoline.beginRestoreSessionForUser(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
-
-        verify(mBackupManagerServiceMock)
-                .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
-    }
-
-    @Test
-    public void opComplete_forwarded() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.opComplete(1, 2);
-
-        verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2);
-    }
-
-    @Test
-    public void getAvailableRestoreTokenForUser_forwarded() {
-        when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
-                .thenReturn(123L);
-
-        assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
-        verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME);
-    }
-
-    @Test
-    public void isAppEligibleForBackupForUser_forwarded() {
-        when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
-                .thenReturn(true);
-
-        assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
-        verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME);
-    }
-
-    @Test
-    public void requestBackupForUser_forwarded() throws Exception {
-        when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
-                mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
-
-        assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES,
-                mBackupObserverMock, mBackupManagerMonitorMock, 123));
-        verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
-                mBackupObserverMock, mBackupManagerMonitorMock, 123);
-    }
-
-    @Test
-    public void requestBackup_forwardedToCallingUserId() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-        when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
-                mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
-
-        assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES,
-                mBackupObserverMock, mBackupManagerMonitorMock, 123));
-        verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
-                mBackupObserverMock, mBackupManagerMonitorMock, 123);
-    }
-
-    @Test
-    public void cancelBackupsForUser_forwarded() throws Exception {
-
-        mTrampoline.cancelBackupsForUser(mUserId);
-
-        verify(mBackupManagerServiceMock).cancelBackups(mUserId);
-    }
-
-    @Test
-    public void cancelBackups_forwardedToCallingUserId() throws Exception {
-        TrampolineTestable.sCallingUserId = mUserId;
-
-        mTrampoline.cancelBackups();
-
-        verify(mBackupManagerServiceMock).cancelBackups(mUserId);
-    }
-
-    @Test
-    public void beginFullBackup_forwarded() throws Exception {
-        FullBackupJob fullBackupJob = new FullBackupJob();
-        when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
-
-        assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
-        verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
-    }
-
-    @Test
-    public void endFullBackup_forwarded() {
-        mTrampoline.endFullBackup(mUserId);
-        verify(mBackupManagerServiceMock).endFullBackup(mUserId);
-    }
-
-    @Test
-    public void dump_callerDoesNotHavePermission_ignored() {
-        when(mContextMock.checkCallingOrSelfPermission(
-                android.Manifest.permission.DUMP)).thenReturn(
-                PackageManager.PERMISSION_DENIED);
-
-        mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
-
-        verifyNoMoreInteractions(mBackupManagerServiceMock);
-    }
-
-    @Test
-    public void dump_callerHasPermission_forwarded() {
-        when(mContextMock.checkCallingOrSelfPermission(
-                android.Manifest.permission.DUMP)).thenReturn(
-                PackageManager.PERMISSION_GRANTED);
-
-        mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, null);
-
-        verify(mBackupManagerServiceMock).dump(mFileDescriptorStub, mPrintWriterMock, null);
-    }
-
-    public void testGetUserForAncestralSerialNumber() {
-        TrampolineTestable.sBackupDisabled = false;
-        Trampoline trampoline = new TrampolineTestable(mContextMock);
-
-        trampoline.getUserForAncestralSerialNumber(0L);
-        verify(mBackupManagerServiceMock).getUserForAncestralSerialNumber(anyInt());
-    }
-
-    public void testGetUserForAncestralSerialNumber_whenDisabled() {
-        TrampolineTestable.sBackupDisabled = true;
-        Trampoline trampoline = new TrampolineTestable(mContextMock);
-
-        trampoline.getUserForAncestralSerialNumber(0L);
-        verify(mBackupManagerServiceMock, never()).getUserForAncestralSerialNumber(anyInt());
-    }
-
-    private static class TrampolineTestable extends Trampoline {
-        static boolean sBackupDisabled = false;
-        static int sCallingUserId = -1;
-        static int sCallingUid = -1;
-        static BackupManagerService sBackupManagerServiceMock = null;
-        static File sSuppressFile = null;
-        static SparseArray<File> sActivatedFiles = new SparseArray<>();
-        static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
-        static UserManager sUserManagerMock = null;
-
-        TrampolineTestable(Context context) {
-            super(context);
-            mService = sBackupManagerServiceMock;
-        }
-
-        @Override
-        protected UserManager getUserManager() {
-            return sUserManagerMock;
-        }
-
-        @Override
-        protected boolean isBackupDisabled() {
-            return sBackupDisabled;
-        }
-
-        @Override
-        protected File getSuppressFileForSystemUser() {
-            return sSuppressFile;
-        }
-
-        @Override
-        protected File getRememberActivatedFileForNonSystemUser(int userId) {
-            return sRememberActivatedFiles.get(userId);
-        }
-
-        @Override
-        protected File getActivatedFileForNonSystemUser(int userId) {
-            return sActivatedFiles.get(userId);
-        }
-
-        protected int binderGetCallingUserId() {
-            return sCallingUserId;
-        }
-
-        @Override
-        protected int binderGetCallingUid() {
-            return sCallingUid;
-        }
-
-        @Override
-        protected void postToHandler(Runnable runnable) {
-            runnable.run();
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
new file mode 100644
index 0000000..ccf3a90
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -0,0 +1,748 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.security.KeyStore;
+
+import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+@SmallTest
+public class BiometricServiceTest {
+
+    private static final String TAG = "BiometricServiceTest";
+
+    private static final String TEST_PACKAGE_NAME = "test_package";
+
+    private static final String ERROR_HW_UNAVAILABLE = "hw_unavailable";
+    private static final String ERROR_NOT_RECOGNIZED = "not_recognized";
+    private static final String ERROR_TIMEOUT = "error_timeout";
+    private static final String ERROR_CANCELED = "error_canceled";
+    private static final String ERROR_UNABLE_TO_PROCESS = "error_unable_to_process";
+    private static final String ERROR_USER_CANCELED = "error_user_canceled";
+
+    private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty";
+
+    private BiometricService mBiometricService;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ContentResolver mContentResolver;
+    @Mock
+    private Resources mResources;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private AppOpsManager mAppOpsManager;
+    @Mock
+    IBiometricServiceReceiver mReceiver1;
+    @Mock
+    IBiometricServiceReceiver mReceiver2;
+    @Mock
+    FingerprintManager mFingerprintManager;
+    @Mock
+    FaceManager mFaceManager;
+
+    private static class MockInjector extends BiometricService.Injector {
+        @Override
+        IActivityManager getActivityManagerService() {
+            return mock(IActivityManager.class);
+        }
+
+        @Override
+        IStatusBarService getStatusBarService() {
+            return mock(IStatusBarService.class);
+        }
+
+        @Override
+        IFingerprintService getFingerprintService() {
+            return mock(IFingerprintService.class);
+        }
+
+        @Override
+        IFaceService getFaceService() {
+            return mock(IFaceService.class);
+        }
+
+        @Override
+        BiometricService.SettingObserver getSettingObserver(Context context, Handler handler,
+                List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
+            return mock(BiometricService.SettingObserver.class);
+        }
+
+        @Override
+        KeyStore getKeyStore() {
+            return mock(KeyStore.class);
+        }
+
+        @Override
+        boolean isDebugEnabled(Context context, int userId) {
+            return false;
+        }
+
+        @Override
+        void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+            // no-op for test
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+        when(mContext.getSystemService(Context.FINGERPRINT_SERVICE))
+                .thenReturn(mFingerprintManager);
+        when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+        when(mContext.getResources()).thenReturn(mResources);
+
+        when(mResources.getString(R.string.biometric_error_hw_unavailable))
+                .thenReturn(ERROR_HW_UNAVAILABLE);
+        when(mResources.getString(R.string.biometric_not_recognized))
+                .thenReturn(ERROR_NOT_RECOGNIZED);
+        when(mResources.getString(R.string.biometric_error_user_canceled))
+                .thenReturn(ERROR_USER_CANCELED);
+    }
+
+    @Test
+    public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+                .thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), eq(ERROR_HW_UNAVAILABLE));
+    }
+
+    @Test
+    public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), any());
+    }
+
+    @Test
+    public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+        when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+    }
+
+    @Test
+    public void testAuthenticateFace_respectsUserSetting()
+            throws Exception {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+        when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        // Disabled in user settings receives onError
+        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+
+        // Enrolled, not disabled in settings, user requires confirmation in settings
+        resetReceiver();
+        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+        when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
+                .thenReturn(true);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mReceiver1, never()).onError(anyInt(), any(String.class));
+        verify(mBiometricService.mFaceService).prepareForAuthentication(
+                eq(true) /* requireConfirmation */,
+                any(IBinder.class),
+                anyLong() /* sessionId */,
+                anyInt() /* userId */,
+                any(IBiometricServiceReceiverInternal.class),
+                anyString() /* opPackageName */,
+                anyInt() /* cookie */,
+                anyInt() /* callingUid */,
+                anyInt() /* callingPid */,
+                anyInt() /* callingUserId */);
+
+        // Enrolled, not disabled in settings, user doesn't require confirmation in settings
+        resetReceiver();
+        when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
+                .thenReturn(false);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+        verify(mBiometricService.mFaceService).prepareForAuthentication(
+                eq(false) /* requireConfirmation */,
+                any(IBinder.class),
+                anyLong() /* sessionId */,
+                anyInt() /* userId */,
+                any(IBiometricServiceReceiverInternal.class),
+                anyString() /* opPackageName */,
+                anyInt() /* cookie */,
+                anyInt() /* callingUid */,
+                anyInt() /* callingPid */,
+                anyInt() /* callingUserId */);
+    }
+
+    @Test
+    public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        // Start testing the happy path
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+        waitForIdle();
+
+        // Creates a pending auth session with the correct initial states
+        assertEquals(mBiometricService.mPendingAuthSession.mState,
+                BiometricService.STATE_AUTH_CALLED);
+
+        // Invokes <Modality>Service#prepareForAuthentication
+        ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mReceiver1, never()).onError(anyInt(), any(String.class));
+        verify(mBiometricService.mFingerprintService).prepareForAuthentication(
+                any(IBinder.class),
+                anyLong() /* sessionId */,
+                anyInt() /* userId */,
+                any(IBiometricServiceReceiverInternal.class),
+                anyString() /* opPackageName */,
+                cookieCaptor.capture() /* cookie */,
+                anyInt() /* callingUid */,
+                anyInt() /* callingPid */,
+                anyInt() /* callingUserId */);
+
+        // onReadyForAuthentication, mCurrentAuthSession state OK
+        mBiometricService.mImpl.onReadyForAuthentication(cookieCaptor.getValue(),
+                anyBoolean() /* requireConfirmation */, anyInt() /* userId */);
+        waitForIdle();
+        assertNull(mBiometricService.mPendingAuthSession);
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+
+        // startPreparedClient invoked
+        verify(mBiometricService.mFingerprintService)
+                .startPreparedClient(cookieCaptor.getValue());
+
+        // StatusBar showBiometricDialog invoked
+        verify(mBiometricService.mStatusBarService).showBiometricDialog(
+                eq(mBiometricService.mCurrentAuthSession.mBundle),
+                any(IBiometricServiceReceiverInternal.class),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                eq(TEST_PACKAGE_NAME));
+
+        // Hardware authenticated
+        mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+                false /* requireConfirmation */,
+                new byte[69] /* HAT */);
+        waitForIdle();
+        // Waiting for SystemUI to send dismissed callback
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTHENTICATED_PENDING_SYSUI);
+        // Notify SystemUI hardware authenticated
+        verify(mBiometricService.mStatusBarService).onBiometricAuthenticated(
+                eq(true) /* authenticated */, eq(null) /* failureReason */);
+
+        // SystemUI sends callback with dismissed reason
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+        waitForIdle();
+        // HAT sent to keystore
+        verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+        // Send onAuthenticated to client
+        verify(mReceiver1).onAuthenticationSucceeded();
+        // Current session becomes null
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testAuthenticate_happyPathWithConfirmation() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                true /* requireConfirmation */);
+
+        // Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
+        // sent to KeyStore yet
+        mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+                true /* requireConfirmation */,
+                new byte[69] /* HAT */);
+        waitForIdle();
+        // Waiting for SystemUI to send confirmation callback
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PENDING_CONFIRM);
+        verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
+
+        // SystemUI sends confirm, HAT is sent to keystore and client is notified.
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+        waitForIdle();
+        verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+        verify(mReceiver1).onAuthenticationSucceeded();
+    }
+
+    @Test
+    public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused()
+            throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAuthenticationFailed();
+        waitForIdle();
+
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+        verify(mReceiver1).onAuthenticationFailed();
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PAUSED);
+    }
+
+    @Test
+    public void testRejectFingerprint_whenAuthenticating_notifiesAndKeepsAuthenticating()
+            throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAuthenticationFailed();
+        waitForIdle();
+
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+        verify(mReceiver1).onAuthenticationFailed();
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+    }
+
+    @Test
+    public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        // Create a new pending auth session but don't start it yet. HAL contract is that previous
+        // one must get ERROR_CANCELED. Simulate that here by creating the pending auth session,
+        // sending ERROR_CANCELED to the current auth session, and then having the second one
+        // onReadyForAuthentication.
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */);
+        waitForIdle();
+
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_CANCELED, ERROR_CANCELED);
+        waitForIdle();
+
+        // Auth session doesn't become null until SystemUI responds that the animation is completed
+        assertNotNull(mBiometricService.mCurrentAuthSession);
+        // ERROR_CANCELED is not sent until SystemUI responded that animation is completed
+        verify(mReceiver1, never()).onError(
+                anyInt(), anyString());
+        verify(mReceiver2, never()).onError(anyInt(), any(String.class));
+
+        // SystemUI dialog closed
+        verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+
+        // After SystemUI notifies that the animation has completed
+        mBiometricService.mInternalReceiver
+                .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+                eq(ERROR_CANCELED));
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        waitForIdle();
+
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PAUSED);
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricAuthenticated(eq(false), eq(ERROR_TIMEOUT));
+        // Timeout does not count as fail as per BiometricPrompt documentation.
+        verify(mReceiver1, never()).onAuthenticationFailed();
+
+        // No pending auth session. Pressing try again will create one.
+        assertNull(mBiometricService.mPendingAuthSession);
+
+        // Pressing "Try again" on SystemUI starts a new auth session.
+        mBiometricService.mInternalReceiver.onTryAgainPressed();
+        waitForIdle();
+
+        // The last one is still paused, and a new one has been created.
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_PAUSED);
+        assertEquals(mBiometricService.mPendingAuthSession.mState,
+                BiometricService.STATE_AUTH_CALLED);
+
+        // Test resuming when hardware becomes ready. SystemUI should not be requested to
+        // show another dialog since it's already showing.
+        resetStatusBar();
+        startPendingAuthSession(mBiometricService);
+        waitForIdle();
+        verify(mBiometricService.mStatusBarService, never()).showBiometricDialog(
+                any(Bundle.class),
+                any(IBiometricServiceReceiverInternal.class),
+                anyInt(),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                anyString());
+    }
+
+    @Test
+    public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireCOnfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+                ERROR_CANCELED);
+        waitForIdle();
+
+        // Client receives error immediately
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+                eq(ERROR_CANCELED));
+        // Dialog is hidden immediately
+        verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+        // Auth session is over
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testErrorFromHal_whileAuthenticating_waitsForSysUIBeforeNotifyingClient()
+            throws Exception {
+        // For errors that show in SystemUI, BiometricService stays in STATE_ERROR_PENDING_SYSUI
+        // until SystemUI notifies us that the dialog is dismissed at which point the current
+        // session is done.
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
+                ERROR_UNABLE_TO_PROCESS);
+        waitForIdle();
+
+        // Sends error to SystemUI and does not notify client yet
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_ERROR_PENDING_SYSUI);
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricError(eq(ERROR_UNABLE_TO_PROCESS));
+        verify(mBiometricService.mStatusBarService, never()).hideBiometricDialog();
+        verify(mReceiver1, never()).onError(anyInt(), anyString());
+
+        // SystemUI animation completed, client is notified, auth session is over
+        mBiometricService.mInternalReceiver
+                .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+                eq(ERROR_UNABLE_TO_PROCESS));
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
+            throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver
+                .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+        waitForIdle();
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
+                eq(ERROR_USER_CANCELED));
+        verify(mBiometricService.mFingerprintService).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                eq(false) /* fromClient */);
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+        waitForIdle();
+
+        verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
+    public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onError(
+                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+                ERROR_TIMEOUT);
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+        waitForIdle();
+
+        verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                anyBoolean());
+    }
+
+    @Test
+    public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                true /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+                true /* requireConfirmation */,
+                new byte[69] /* HAT */);
+        mBiometricService.mInternalReceiver.onDialogDismissed(
+                BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+        waitForIdle();
+
+        // doesn't send cancel to HAL
+        verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+                any(),
+                any(),
+                anyInt(),
+                anyInt(),
+                anyInt(),
+                anyBoolean());
+        verify(mReceiver1).onError(
+                eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
+                eq(ERROR_USER_CANCELED));
+        assertNull(mBiometricService.mCurrentAuthSession);
+    }
+
+    @Test
+    public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */);
+
+        mBiometricService.mInternalReceiver.onAcquired(
+                FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+                FINGERPRINT_ACQUIRED_SENSOR_DIRTY);
+        waitForIdle();
+
+        // Sends to SysUI and stays in authenticating state
+        verify(mBiometricService.mStatusBarService)
+                .onBiometricHelp(eq(FINGERPRINT_ACQUIRED_SENSOR_DIRTY));
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+    }
+
+    // Helper methods
+
+    private void setupAuthForOnly(int modality) {
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+                .thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
+
+        if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
+            when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+                    .thenReturn(true);
+            when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+            when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+        } else if (modality == BiometricAuthenticator.TYPE_FACE) {
+            when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+            when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+            when(mFaceManager.isHardwareDetected()).thenReturn(true);
+        } else {
+            fail("Unknown modality: " + modality);
+        }
+
+        mBiometricService = new BiometricService(mContext, new MockInjector());
+        mBiometricService.onStart();
+
+        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+    }
+
+    private void resetReceiver() {
+        mReceiver1 = mock(IBiometricServiceReceiver.class);
+        mReceiver2 = mock(IBiometricServiceReceiver.class);
+    }
+
+    private void resetStatusBar() {
+        mBiometricService.mStatusBarService = mock(IStatusBarService.class);
+    }
+
+    private void invokeAuthenticateAndStart(IBiometricService.Stub service,
+            IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+        // Request auth, creates a pending session
+        invokeAuthenticate(service, receiver, requireConfirmation);
+        waitForIdle();
+
+        startPendingAuthSession(mBiometricService);
+        waitForIdle();
+    }
+
+    private static void startPendingAuthSession(BiometricService service) throws Exception {
+        // Get the cookie so we can pretend the hardware is ready to authenticate
+        // Currently we only support single modality per auth
+        assertEquals(service.mPendingAuthSession.mModalitiesWaiting.values().size(), 1);
+        final int cookie = service.mPendingAuthSession.mModalitiesWaiting.values()
+                .iterator().next();
+        assertNotEquals(cookie, 0);
+
+        service.mImpl.onReadyForAuthentication(cookie,
+                anyBoolean() /* requireConfirmation */, anyInt() /* userId */);
+    }
+
+    private static void invokeAuthenticate(IBiometricService.Stub service,
+            IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+        service.authenticate(
+                new Binder() /* token */,
+                0 /* sessionId */,
+                0 /* userId */,
+                receiver,
+                TEST_PACKAGE_NAME /* packageName */,
+                createTestBiometricPromptBundle(requireConfirmation),
+                null /* IBiometricConfirmDeviceCredentialCallback */);
+    }
+
+    private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation) {
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+        return bundle;
+    }
+
+    private static int getCookieForCurrentSession(BiometricService.AuthSession session) {
+        assertEquals(session.mModalitiesMatched.values().size(), 1);
+        return session.mModalitiesMatched.values().iterator().next();
+    }
+
+    private static void waitForIdle() {
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 09ae3a2..ba12b73 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -139,7 +139,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
-import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
 
 import com.google.common.util.concurrent.AbstractFuture;
@@ -293,7 +293,7 @@
     };
 
     private void registerLocalServices() {
-        addLocalServiceMock(DeviceIdleController.LocalService.class);
+        addLocalServiceMock(DeviceIdleInternal.class);
 
         final UsageStatsManagerInternal usageStats =
                 addLocalServiceMock(UsageStatsManagerInternal.class);
@@ -442,7 +442,7 @@
         // Added in registerLocalServices()
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.removeServiceForTest(PowerManagerInternal.class);
-        LocalServices.removeServiceForTest(DeviceIdleController.LocalService.class);
+        LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
         LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 75e5847..819091c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -19,12 +19,12 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.AppOpsManager;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
@@ -54,10 +54,7 @@
     IPermissionManager mPermissionManagerMock;
 
     @Mock
-    AppsFilter.ConfigProvider mConfigProviderMock;
-
-    @Mock
-    AppOpsManager mAppOpsManager;
+    AppsFilter.FeatureConfig mFeatureConfigMock;
 
     private Map<String, PackageParser.Package> mExisting = new ArrayMap<>();
 
@@ -108,16 +105,23 @@
         when(mPermissionManagerMock
                 .checkPermission(anyString(), anyString(), anyInt()))
                 .thenReturn(PackageManager.PERMISSION_DENIED);
-        when(mConfigProviderMock.isEnabled()).thenReturn(true);
-        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
-                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
+        when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
+        when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
+                .thenReturn(true);
+    }
+
+    @Test
+    public void testSystemReadyPropogates() throws Exception {
+        final AppsFilter appsFilter =
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
+        appsFilter.onSystemReady();
+        verify(mFeatureConfigMock).onSystemReady();
     }
 
     @Test
     public void testQueriesAction_FilterMatches() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
-                        new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package", new IntentFilter("TEST_ACTION"))).build();
@@ -130,8 +134,7 @@
     @Test
     public void testQueriesAction_NoMatchingAction_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
-                        new String[]{}, false);
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package")).build();
@@ -144,7 +147,7 @@
     @Test
     public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -158,7 +161,7 @@
     @Test
     public void testNoQueries_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -171,7 +174,7 @@
     @Test
     public void testForceQueryable_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target =
@@ -186,7 +189,7 @@
     @Test
     public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{"com.some.package"}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
@@ -201,7 +204,7 @@
     @Test
     public void testForceQueryableByDevice_NonSystemCaller_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{"com.some.package"}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -215,7 +218,7 @@
     @Test
     public void testSystemQueryable_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, true /* system force queryable */);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
@@ -230,7 +233,7 @@
     @Test
     public void testQueriesPackage_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -241,55 +244,11 @@
     }
 
     @Test
-    public void testNoQueries_AppOpModeDeny_Filters() {
-        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
-                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
-        final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
-                        new String[]{}, false);
-
-        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
-        PackageSetting calling = simulateAddPackage(appsFilter,
-                pkg("com.some.other.package")).build();
-
-        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
-    }
-
-    @Test
-    public void testNoQueries_AppOpModeAllow_DoesntFilter() {
-        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
-                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ALLOWED);
-        final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
-                        new String[]{}, false);
-
-        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
-        PackageSetting calling = simulateAddPackage(appsFilter,
-                pkg("com.some.other.package")).build();
-
-        assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
-    }
-
-    @Test
-    public void testNoQueries_AppOpModeIgnore_Filters() {
-        when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
-                DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_IGNORED);
-        final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
-                        new String[]{}, false);
-
-        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
-        PackageSetting calling = simulateAddPackage(appsFilter,
-                pkg("com.some.other.package")).build();
-
-        assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
-    }
-
-    @Test
     public void testNoQueries_FeatureOff_DoesntFilter() {
-        when(mConfigProviderMock.isEnabled()).thenReturn(false);
+        when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
+                .thenReturn(false);
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -302,7 +261,7 @@
     @Test
     public void testSystemUid_DoesntFilter() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -315,7 +274,7 @@
     @Test
     public void testNonSystemUid_NoCallingSetting_Filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -326,7 +285,7 @@
     @Test
     public void testNoTargetPackage_filters() {
         final AppsFilter appsFilter =
-                new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+                new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
                         new String[]{}, false);
 
         PackageSetting target = new PackageSettingBuilder()
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 7986055..8cb5197 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.rollback;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
@@ -58,8 +59,8 @@
         // All users are unlocked so we should snapshot data for them.
         doReturn(true).when(helper).isUserCredentialLocked(eq(10));
         doReturn(true).when(helper).isUserCredentialLocked(eq(11));
-        PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar", new int[]{10, 11});
-        helper.snapshotAppData(5, info);
+        PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar");
+        helper.snapshotAppData(5, info, new int[]{10, 11});
 
         assertEquals(2, info.getPendingBackups().size());
         assertEquals(10, info.getPendingBackups().get(0));
@@ -79,8 +80,8 @@
         doReturn(true).when(helper).isUserCredentialLocked(eq(11));
         when(installer.snapshotAppData(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(239L);
 
-        PackageRollbackInfo info2 = createPackageRollbackInfo("com.foo.bar", new int[]{10, 11});
-        helper.snapshotAppData(7, info2);
+        PackageRollbackInfo info2 = createPackageRollbackInfo("com.foo.bar");
+        helper.snapshotAppData(7, info2, new int[]{10, 11});
         assertEquals(1, info2.getPendingBackups().size());
         assertEquals(11, info2.getPendingBackups().get(0));
 
@@ -234,22 +235,22 @@
         wasRecentlyRestored.getPendingRestores().add(
                 new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo"));
 
-        RollbackData dataWithPendingBackup = new RollbackData(101, new File("/does/not/exist"), -1);
+        Rollback dataWithPendingBackup = new Rollback(101, new File("/does/not/exist"), -1);
         dataWithPendingBackup.info.getPackages().add(pendingBackup);
 
-        RollbackData dataWithRecentRestore = new RollbackData(17239, new File("/does/not/exist"),
+        Rollback dataWithRecentRestore = new Rollback(17239, new File("/does/not/exist"),
                 -1);
         dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored);
 
-        RollbackData dataForDifferentUser = new RollbackData(17239, new File("/does/not/exist"),
+        Rollback dataForDifferentUser = new Rollback(17239, new File("/does/not/exist"),
                 -1);
         dataForDifferentUser.info.getPackages().add(ignoredInfo);
 
-        RollbackData dataForRestore = new RollbackData(17239, new File("/does/not/exist"), -1);
+        Rollback dataForRestore = new Rollback(17239, new File("/does/not/exist"), -1);
         dataForRestore.info.getPackages().add(pendingRestore);
         dataForRestore.info.getPackages().add(wasRecentlyRestored);
 
-        Set<RollbackData> changed = helper.commitPendingBackupAndRestoreForUser(37,
+        Set<Rollback> changed = helper.commitPendingBackupAndRestoreForUser(37,
                 Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser,
                     dataForRestore));
         InOrder inOrder = Mockito.inOrder(installer);
@@ -264,7 +265,7 @@
         assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37));
         assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37));
 
-        // Check that changed returns correct RollbackData.
+        // Check that changed returns correct Rollback.
         assertEquals(3, changed.size());
         assertTrue(changed.contains(dataWithPendingBackup));
         assertTrue(changed.contains(dataWithRecentRestore));
@@ -278,4 +279,15 @@
 
         inOrder.verifyNoMoreInteractions();
     }
+
+    @Test
+    public void snapshotAddDataSavesSnapshottedUsersToInfo() {
+        Installer installer = mock(Installer.class);
+        AppDataRollbackHelper helper = new AppDataRollbackHelper(installer);
+
+        PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar");
+        helper.snapshotAppData(5, info, new int[]{10, 11});
+
+        assertArrayEquals(info.getSnapshottedUsers().toArray(), new int[]{10, 11});
+    }
 }
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 c1c0a30..57caa1d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2766,6 +2766,18 @@
     }
 
     @Test
+    public void testReadPolicyXml_readSnoozedNotificationsFromXml() throws Exception {
+        final String upgradeXml = "<notification-policy version=\"1\">"
+                + "<snoozed-notifications>></snoozed-notifications>"
+                + "</notification-policy>";
+        mService.readPolicyXml(
+                new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
+                false,
+                UserHandle.USER_ALL);
+        verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class));
+    }
+
+    @Test
     public void testReadPolicyXml_readApprovedServicesFromSettings() throws Exception {
         final String preupgradeXml = "<notification-policy version=\"1\">"
                 + "<ranking></ranking>"
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 2e7277f..36175a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -18,6 +18,7 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -37,9 +38,11 @@
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.IntArray;
+import android.util.Xml;
 
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
@@ -48,6 +51,15 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -69,6 +81,117 @@
     }
 
     @Test
+    public void testWriteXMLformattedCorrectly_testReadingCorrectTime()
+            throws XmlPullParserException, IOException {
+        final String max_time_str = Long.toString(Long.MAX_VALUE);
+        final String xml_string = "<snoozed-notifications>"
+                + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key\" time=\"" + max_time_str + "\"/>"
+                + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
+                + "</snoozed-notifications>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml_string.getBytes())), null);
+        mSnoozeHelper.readXml(parser);
+        assertTrue("Should read the notification time from xml and it should be more than zero",
+                0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        0, "pkg", "key").doubleValue());
+    }
+
+    @Test
+    public void testWriteXMLformattedCorrectly_testCorrectContextURI()
+            throws XmlPullParserException, IOException {
+        final String max_time_str = Long.toString(Long.MAX_VALUE);
+        final String xml_string = "<snoozed-notifications>"
+                + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key\" id=\"uri\"/>"
+                + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+                + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>"
+                + "</snoozed-notifications>";
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml_string.getBytes())), null);
+        mSnoozeHelper.readXml(parser);
+        assertEquals("Should read the notification context from xml and it should be `uri",
+                "uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+                        0, "pkg", "key"));
+    }
+
+    @Test
+    public void testReadValidSnoozedFromCorrectly_timeDeadline()
+            throws XmlPullParserException, IOException {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 999999999);
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mSnoozeHelper.writeXml(serializer);
+        serializer.endDocument();
+        serializer.flush();
+
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+        mSnoozeHelper.readXml(parser);
+        assertTrue("Should read the notification time from xml and it should be more than zero",
+                0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        0, "pkg", r.getKey()).doubleValue());
+    }
+
+
+    @Test
+    public void testReadExpiredSnoozedNotification() throws
+            XmlPullParserException, IOException, InterruptedException {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 0);
+       // Thread.sleep(100);
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mSnoozeHelper.writeXml(serializer);
+        serializer.endDocument();
+        serializer.flush();
+        Thread.sleep(10);
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+        mSnoozeHelper.readXml(parser);
+        int systemUser = UserHandle.SYSTEM.getIdentifier();
+        assertTrue("Should see a past time returned",
+                System.currentTimeMillis() >  mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        systemUser, "pkg", r.getKey()).longValue());
+    }
+
+    @Test
+    public void testCleanupContextShouldRemovePersistedRecord() {
+        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, "context");
+        mSnoozeHelper.cleanupPersistedContext(r.sbn.getKey());
+        assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+                r.getUser().getIdentifier(),
+                r.sbn.getPackageName(),
+                r.sbn.getKey()
+        ));
+    }
+
+    @Test
+    public void testReadNoneSnoozedNotification() throws XmlPullParserException,
+            IOException, InterruptedException {
+        NotificationRecord r = getNotificationRecord(
+                "pkg", 1, "one", UserHandle.SYSTEM);
+        mSnoozeHelper.snooze(r, 0);
+
+        assertEquals("should see a zero value for unsnoozed notification",
+                0L,
+                mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+                        UserHandle.SYSTEM.getIdentifier(),
+                        "not_my_package", r.getKey()).longValue());
+    }
+
+    @Test
     public void testSnoozeForTime() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         mSnoozeHelper.snooze(r, 1000);
@@ -84,7 +207,7 @@
     @Test
     public void testSnooze() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
-        mSnoozeHelper.snooze(r);
+        mSnoozeHelper.snooze(r, (String) null);
         verify(mAm, never()).setExactAndAllowWhileIdle(
                 anyInt(), anyLong(), any(PendingIntent.class));
         assertTrue(mSnoozeHelper.isSnoozed(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 63b9198..6c78f6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -28,6 +28,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
@@ -140,7 +141,8 @@
     public void testNotResumeHomeStackOnRemovingDisplay() {
         // Create a display which supports system decoration and allows reparenting stacks to
         // another display when the display is removed.
-        final ActivityDisplay display = spy(createNewActivityDisplay());
+        final ActivityDisplay display = createNewActivityDisplay();
+        spyOn(display);
         doReturn(false).when(display).shouldDestroyContentOnRemove();
         doReturn(true).when(display).supportsSystemDecorations();
         mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
@@ -304,14 +306,18 @@
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
         final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, ON_TOP);
-        final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack1).setTaskId(1).build();
-        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack2).setTaskId(2).build();
-        final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack3).setTaskId(3).build();
-        final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack(
-                stack4).setTaskId(4).build();
+        final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack1)
+                .build();
+        final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack2)
+                .build();
+        final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack3)
+                .build();
+        final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(stack4)
+                .build();
 
         // Reordering stacks while removing stacks.
         doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 23bae88..977dd8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -61,13 +61,13 @@
     private ActivityMetricsLaunchObserver mLaunchObserver;
     private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
 
-    private TestActivityStack mStack;
+    private ActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivityRecord;
     private ActivityRecord mActivityRecordTrampoline;
 
     @Before
-    public void setUpAMLO() throws Exception {
+    public void setUpAMLO() {
         mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
 
         // ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
@@ -78,15 +78,19 @@
 
         // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
         // This seems to be the easiest way to create an ActivityRecord.
-        mStack = mRootActivityContainer.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
-        mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+        mStack = new StackBuilder(mRootActivityContainer)
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+                .setOnTop(true)
+                .setCreateActivity(true)
+                .build();
+        mTask = mStack.topTask();
+        mActivityRecord = mTask.getTopActivity();
         mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
     }
 
     @After
-    public void tearDownAMLO() throws Exception {
+    public void tearDownAMLO() {
         if (mLaunchObserverRegistry != null) {  // Don't NPE if setUp failed.
             mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7b252cb..0f04788 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,6 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.os.Process.NOBODY_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Surface.ROTATION_0;
@@ -29,6 +35,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
@@ -71,6 +78,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.MergedConfiguration;
 import android.util.MutableBoolean;
+import android.view.DisplayInfo;
 import android.view.IRemoteAnimationFinishedCallback;
 import android.view.IRemoteAnimationRunner.Stub;
 import android.view.RemoteAnimationAdapter;
@@ -79,7 +87,6 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.R;
-import com.android.server.wm.utils.WmDisplayCutout;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -96,13 +103,13 @@
 @MediumTest
 @Presubmit
 public class ActivityRecordTests extends ActivityTestsBase {
-    private TestActivityStack mStack;
+    private ActivityStack mStack;
     private TaskRecord mTask;
     private ActivityRecord mActivity;
 
     @Before
     public void setUp() throws Exception {
-        mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+        mStack = new StackBuilder(mRootActivityContainer).build();
         mTask = mStack.getChildAt(0);
         mActivity = mTask.getTopActivity();
 
@@ -113,13 +120,13 @@
     @Test
     public void testStackCleanupOnClearingTask() {
         mActivity.setTask(null);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+        verify(mStack, times(1)).onActivityRemovedFromStack(any());
     }
 
     @Test
     public void testStackCleanupOnActivityRemoval() {
         mTask.removeActivity(mActivity);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(),  1);
+        verify(mStack, times(1)).onActivityRemovedFromStack(any());
     }
 
     @Test
@@ -134,7 +141,7 @@
         final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
                 .build();
         mActivity.reparent(newTask, 0, null /*reason*/);
-        assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
+        verify(mStack, times(0)).onActivityRemovedFromStack(any());
     }
 
     @Test
@@ -181,7 +188,7 @@
         assertTrue(mActivity.isState(STARTED));
 
         mStack.mTranslucentActivityWaiting = null;
-        topActivity.changeWindowTranslucency(false);
+        topActivity.setOccludesParent(false);
         mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
         mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
         assertTrue(mActivity.isState(STARTED));
@@ -261,8 +268,8 @@
     public void testNewParentConfigurationIncrementsSeq() {
         final Configuration newConfig = new Configuration(
                 mTask.getRequestedOverrideConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
 
         final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
         mTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -277,7 +284,7 @@
                 .getRequestedOverrideConfiguration();
 
         final Configuration newConfig = new Configuration();
-        newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = ORIENTATION_PORTRAIT;
 
         final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
         mActivity.onRequestedOverrideConfigurationChanged(newConfig);
@@ -293,10 +300,11 @@
         mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 mActivity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(mTask.getConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE
+                : ORIENTATION_PORTRAIT;
         mTask.onRequestedOverrideConfigurationChanged(newConfig);
 
         mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -315,13 +323,14 @@
         mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 mActivity.getConfiguration()));
 
-        mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+        mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(mTask.getConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+        newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+                ? ORIENTATION_LANDSCAPE
+                : ORIENTATION_PORTRAIT;
         mTask.onRequestedOverrideConfigurationChanged(newConfig);
 
-        doReturn(true).when(mTask.getTask()).isDragResizing();
+        doReturn(true).when(mTask.mTask).isDragResizing();
 
         mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 
@@ -355,30 +364,39 @@
 
     @Test
     public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
+                .build();
         mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
 
-        mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
         mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                 mActivity.getConfiguration()));
 
-        mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
         final Configuration newConfig = new Configuration(mActivity.getConfiguration());
-        newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                ? Configuration.ORIENTATION_LANDSCAPE
-                : Configuration.ORIENTATION_PORTRAIT;
+        final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+        final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+        if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+            newConfig.orientation = ORIENTATION_LANDSCAPE;
+            newConfig.screenWidthDp = longSide;
+            newConfig.screenHeightDp = shortSide;
+        } else {
+            newConfig.orientation = ORIENTATION_PORTRAIT;
+            newConfig.screenWidthDp = shortSide;
+            newConfig.screenHeightDp = longSide;
+        }
 
         // Mimic the behavior that display doesn't handle app's requested orientation.
-        doAnswer(invocation -> {
-            mTask.onConfigurationChanged(newConfig);
-            return null;
-        }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any());
+        final DisplayContent dc = mTask.mTask.getDisplayContent();
+        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+        doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
 
         final int requestedOrientation;
         switch (newConfig.orientation) {
-            case Configuration.ORIENTATION_LANDSCAPE:
-                requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+            case ORIENTATION_LANDSCAPE:
+                requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
                 break;
-            case Configuration.ORIENTATION_PORTRAIT:
+            case ORIENTATION_PORTRAIT:
                 requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
                 break;
             default:
@@ -421,24 +439,33 @@
 
     @Test
     public void testPushConfigurationWhenLaunchTaskBehind() throws Exception {
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setLaunchTaskBehind(true)
+                .setConfigChanges(CONFIG_ORIENTATION)
+                .build();
         mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
 
-        final TestActivityStack stack = (TestActivityStack) new StackBuilder(mRootActivityContainer)
-                .build();
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         try {
-            stack.setIsTranslucent(false);
+            doReturn(false).when(stack).isStackTranslucent(any());
             assertFalse(mStack.shouldBeVisible(null /* starting */));
 
-            mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
             mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
                     mActivity.getConfiguration()));
 
-            mActivity.mLaunchTaskBehind = true;
-            mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
             final Configuration newConfig = new Configuration(mActivity.getConfiguration());
-            newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
-                    ? Configuration.ORIENTATION_LANDSCAPE
-                    : Configuration.ORIENTATION_PORTRAIT;
+            final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+            final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+            if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+                newConfig.orientation = ORIENTATION_LANDSCAPE;
+                newConfig.screenWidthDp = longSide;
+                newConfig.screenHeightDp = shortSide;
+            } else {
+                newConfig.orientation = ORIENTATION_PORTRAIT;
+                newConfig.screenWidthDp = shortSide;
+                newConfig.screenHeightDp = longSide;
+            }
 
             mTask.onConfigurationChanged(newConfig);
 
@@ -457,7 +484,7 @@
     @Test
     public void testShouldPauseWhenMakeClientVisible() {
         ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
-        topActivity.changeWindowTranslucency(false);
+        topActivity.setOccludesParent(false);
         mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
         mActivity.makeClientVisible();
         assertEquals(STARTED, mActivity.getState());
@@ -468,6 +495,7 @@
         setupDisplayContentForCompatDisplayInsets();
         final int decorHeight = 200; // e.g. The device has cutout.
         final DisplayPolicy policy = setupDisplayAndParentSize(600, 800).getDisplayPolicy();
+        spyOn(policy);
         doAnswer(invocationOnMock -> {
             final int rotation = invocationOnMock.<Integer>getArgument(0);
             final Rect insets = invocationOnMock.<Rect>getArgument(4);
@@ -482,7 +510,7 @@
 
         doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
                 .when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
         ensureActivityConfiguration();
         // The parent configuration doesn't change since the first resolved configuration, so the
@@ -506,19 +534,28 @@
     @Test
     public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
         // Initialize different bounds on a new display.
-        final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
-        newDisplay.getWindowConfiguration().setAppBounds(new Rect(0, 0, 1000, 2000));
-        newDisplay.getConfiguration().densityDpi = 300;
+        final Rect newDisplayBounds = new Rect(0, 0, 1000, 2000);
+        DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
+        info.logicalWidth = newDisplayBounds.width();
+        info.logicalHeight = newDisplayBounds.height();
+        info.logicalDensityDpi = 300;
+
+        final ActivityDisplay newDisplay =
+                addNewActivityDisplayAt(info, ActivityDisplay.POSITION_TOP);
 
         mTask.getConfiguration().densityDpi = 200;
-        prepareFixedAspectRatioUnresizableActivity();
+        mActivity = new ActivityBuilder(mService)
+                .setTask(mTask)
+                .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+                .setMaxAspectRatio(1.5f)
+                .build();
 
         final Rect originalBounds = new Rect(mActivity.getBounds());
         final int originalDpi = mActivity.getConfiguration().densityDpi;
 
         // Move the non-resizable activity to the new display.
         mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
-        ensureActivityConfiguration();
 
         assertEquals(originalBounds, mActivity.getBounds());
         assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
@@ -531,9 +568,9 @@
         when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
         mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
-        mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+        mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
         mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         ensureActivityConfiguration();
         final Rect originalBounds = new Rect(mActivity.getBounds());
 
@@ -579,7 +616,7 @@
         mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
 
         // Simulate the display changes orientation.
-        doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION
+        doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
                 | ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
                         .when(display).getLastOverrideConfigurationChanges();
         mActivity.onConfigurationChanged(mTask.getConfiguration());
@@ -754,24 +791,17 @@
     /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
     private void prepareFixedAspectRatioUnresizableActivity() {
         setupDisplayContentForCompatDisplayInsets();
-        when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
-                ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+        mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
         mActivity.info.maxAspectRatio = 1.5f;
         ensureActivityConfiguration();
     }
 
     private void setupDisplayContentForCompatDisplayInsets() {
         final Rect displayBounds = mStack.getDisplay().getBounds();
-        final DisplayContent displayContent = setupDisplayAndParentSize(
-                displayBounds.width(), displayBounds.height());
-        doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy();
-        doReturn(mock(WmDisplayCutout.class)).when(displayContent)
-                .calculateDisplayCutoutForRotation(anyInt());
+        setupDisplayAndParentSize(displayBounds.width(), displayBounds.height());
     }
 
     private DisplayContent setupDisplayAndParentSize(int width, int height) {
-        // The DisplayContent is already a mocked object.
         final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
         displayContent.mBaseDisplayWidth = width;
         displayContent.mBaseDisplayHeight = height;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index e5278d8..ff7b1fa 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -29,7 +29,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
@@ -44,6 +44,7 @@
 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -83,8 +84,9 @@
     @Before
     public void setUp() throws Exception {
         mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
-        mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */));
+        mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */);
+        spyOn(mStack);
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
     }
 
@@ -140,10 +142,8 @@
 
         final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
-
-        mTask.removeActivity(r);
-        destTask.addActivityToTop(r);
+        mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false,
+                "testResumedActivityFromActivityReparenting");
 
         assertNull(mStack.getResumedActivity());
         assertEquals(r, destStack.getResumedActivity());
@@ -313,45 +313,50 @@
 
     @Test
     public void testShouldBeVisible_Fullscreen() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(pinnedStack)
+                .build();
 
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
 
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         // Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
         // should be visible since it is always on-top.
-        fullscreenStack.setIsTranslucent(false);
+        doReturn(false).when(fullscreenStack).isStackTranslucent(any());
         assertFalse(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
         assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
 
         // Home stack should be visible behind a translucent fullscreen stack.
-        fullscreenStack.setIsTranslucent(true);
+        doReturn(true).when(fullscreenStack).isStackTranslucent(any());
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
     }
 
     @Test
     public void testShouldBeVisible_SplitScreen() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         // Home stack should always be fullscreen for this test.
-        homeStack.setSupportsSplitScreen(false);
-        final TestActivityStack splitScreenPrimary =
+        doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
+        final ActivityStack splitScreenPrimary =
                 createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TestActivityStack splitScreenSecondary =
+        final ActivityStack splitScreenSecondary =
                 createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Home stack shouldn't be visible if both halves of split-screen are opaque.
-        splitScreenPrimary.setIsTranslucent(false);
-        splitScreenSecondary.setIsTranslucent(false);
+        doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+        doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
         assertFalse(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -362,7 +367,7 @@
                 splitScreenSecondary.getVisibility(null /* starting */));
 
         // Home stack should be visible if one of the halves of split-screen is translucent.
-        splitScreenPrimary.setIsTranslucent(true);
+        doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -373,12 +378,12 @@
         assertEquals(STACK_VISIBILITY_VISIBLE,
                 splitScreenSecondary.getVisibility(null /* starting */));
 
-        final TestActivityStack splitScreenSecondary2 =
+        final ActivityStack splitScreenSecondary2 =
                 createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         // First split-screen secondary shouldn't be visible behind another opaque split-split
         // secondary.
-        splitScreenSecondary2.setIsTranslucent(false);
+        doReturn(false).when(splitScreenSecondary2).isStackTranslucent(any());
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
         assertEquals(STACK_VISIBILITY_INVISIBLE,
@@ -388,7 +393,7 @@
 
         // First split-screen secondary should be visible behind another translucent split-screen
         // secondary.
-        splitScreenSecondary2.setIsTranslucent(true);
+        doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
         assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -396,11 +401,11 @@
         assertEquals(STACK_VISIBILITY_VISIBLE,
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
-        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
 
         // Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
-        assistantStack.setIsTranslucent(false);
+        doReturn(false).when(assistantStack).isStackTranslucent(any());
         assertTrue(assistantStack.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -415,7 +420,7 @@
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // Split-screen stacks should be visible behind a translucent fullscreen stack.
-        assistantStack.setIsTranslucent(true);
+        doReturn(true).when(assistantStack).isStackTranslucent(any());
         assertTrue(assistantStack.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -430,9 +435,9 @@
                 splitScreenSecondary2.getVisibility(null /* starting */));
 
         // Assistant stack shouldn't be visible behind translucent split-screen stack
-        assistantStack.setIsTranslucent(false);
-        splitScreenPrimary.setIsTranslucent(true);
-        splitScreenSecondary2.setIsTranslucent(true);
+        doReturn(false).when(assistantStack).isStackTranslucent(any());
+        doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
+        doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
         splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
         splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
         assertFalse(assistantStack.shouldBeVisible(null /* starting */));
@@ -450,10 +455,10 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucent() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
@@ -465,13 +470,13 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final TestActivityStack opaqueStack =
+        final ActivityStack opaqueStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
@@ -483,13 +488,13 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack opaqueStack =
+        final ActivityStack opaqueStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
@@ -502,10 +507,10 @@
 
     @Test
     public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
-        final TestActivityStack bottomTranslucentStack =
+        final ActivityStack bottomTranslucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
 
@@ -517,10 +522,10 @@
 
     @Test
     public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
-        final TestActivityStack bottomTranslucentStack =
+        final ActivityStack bottomTranslucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
-        final TestActivityStack opaqueStack =
+        final ActivityStack opaqueStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
 
@@ -531,10 +536,10 @@
 
     @Test
     public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
-        final TestActivityStack bottomStack =
+        final ActivityStack bottomStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         false /* translucent */);
-        final TestActivityStack translucentStack =
+        final ActivityStack translucentStack =
                 createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
                         true /* translucent */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
@@ -544,22 +549,34 @@
                 bottomStack.getVisibility(null /* starting */));
         assertEquals(STACK_VISIBILITY_VISIBLE,
                 translucentStack.getVisibility(null /* starting */));
+        // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+                .setCreateTask(true)
+                .setStack(pinnedStack)
+                .build();
         assertEquals(STACK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
     }
 
     @Test
     public void testShouldBeVisible_Finishing() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(
+        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        if (topRunningHomeActivity == null) {
+            topRunningHomeActivity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        }
+
+        final ActivityStack translucentStack = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        translucentStack.setIsTranslucent(true);
+        doReturn(true).when(translucentStack).isStackTranslucent(any());
 
         assertTrue(homeStack.shouldBeVisible(null /* starting */));
         assertTrue(translucentStack.shouldBeVisible(null /* starting */));
 
-        final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
         topRunningHomeActivity.finishing = true;
         final ActivityRecord topRunningTranslucentActivity =
                 translucentStack.topRunningActivityLocked();
@@ -577,13 +594,13 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack).isStackTranslucent(any());
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -596,13 +613,13 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(true);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(true).when(fullscreenStack).isStackTranslucent(any());
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -615,13 +632,13 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack).isStackTranslucent(any());
 
         // Ensure we don't move the home stack if it is already on top
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -634,20 +651,20 @@
     public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
 
         // Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
         // pinned stack
@@ -661,18 +678,18 @@
             testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(true);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+        doReturn(true).when(fullscreenStack2).isStackTranslucent(any());
 
         // Ensure that we move the home stack behind the bottom most non-translucent fullscreen
         // stack
@@ -685,18 +702,18 @@
     public void testMoveHomeStackBehindStack_BehindHomeStack() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
-        homeStack.setIsTranslucent(false);
-        fullscreenStack1.setIsTranslucent(false);
-        fullscreenStack2.setIsTranslucent(false);
+        doReturn(false).when(homeStack).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+        doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
 
         // Ensure we don't move the home stack behind itself
         int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -708,19 +725,19 @@
     public void testMoveHomeStackBehindStack() {
         mDefaultDisplay.removeChild(mStack);
 
-        final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
+        final ActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
         mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
@@ -735,13 +752,13 @@
 
     @Test
     public void testSetAlwaysOnTop() {
-        final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(homeStack));
 
-        final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+        final ActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         alwaysOnTopStack.setAlwaysOnTop(true);
@@ -749,13 +766,13 @@
         // Ensure (non-pinned) always on top stack is put below pinned stack.
         assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack));
 
-        final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+        final ActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         // Ensure non always on top stack is put below always on top stacks.
         assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack));
 
-        final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+        final ActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
         alwaysOnTopStack2.setAlwaysOnTop(true);
@@ -780,18 +797,18 @@
 
     @Test
     public void testSplitScreenMoveToFront() {
-        final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
+        final ActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
+        final ActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
                 true /* onTop */);
-        final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
 
-        splitScreenPrimary.setIsTranslucent(false);
-        splitScreenSecondary.setIsTranslucent(false);
-        assistantStack.setIsTranslucent(false);
+        doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+        doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
+        doReturn(false).when(assistantStack).isStackTranslucent(any());
 
         assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
         assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -804,18 +821,18 @@
         assertFalse(assistantStack.shouldBeVisible(null /* starting */));
     }
 
-    private TestActivityStack createStandardStackForVisibilityTest(int windowingMode,
+    private ActivityStack createStandardStackForVisibilityTest(int windowingMode,
             boolean translucent) {
-        final TestActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+        final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        stack.setIsTranslucent(translucent);
+        doReturn(translucent).when(stack).isStackTranslucent(any());
         return stack;
     }
 
     @SuppressWarnings("TypeParameterUnusedInFormals")
     private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
             ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
-        final T stack;
+        final ActivityStack stack;
         if (activityType == ACTIVITY_TYPE_HOME) {
             // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
             stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
@@ -825,11 +842,15 @@
                 mDefaultDisplay.positionChildAtBottom(stack);
             }
         } else {
-            stack = display.createStack(windowingMode, activityType, onTop);
-            final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
-                    .setCreateTask(true).build();
+            stack = new StackBuilder(mRootActivityContainer)
+                    .setDisplay(display)
+                    .setWindowingMode(windowingMode)
+                    .setActivityType(activityType)
+                    .setOnTop(onTop)
+                    .setCreateActivity(true)
+                    .build();
         }
-        return stack;
+        return (T) stack;
     }
 
     @Test
@@ -961,11 +982,16 @@
 
     @Test
     public void testAdjustFocusedStackToHomeWhenNoActivity() {
+        final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
+        TaskRecord homeTask = homeStask.topTask();
+        if (homeTask == null) {
+            // Create home task if there isn't one.
+            homeTask = new TaskBuilder(mSupervisor).setStack(homeStask).build();
+        }
+
         final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
         mStack.moveToFront("testAdjustFocusedStack");
 
-        final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
-        final TaskRecord homeTask = homeStask.topTask();
         // Simulate that home activity has not been started or is force-stopped.
         homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING);
 
@@ -981,6 +1007,14 @@
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
 
+        ActivityRecord activity = homeStack.topRunningActivityLocked();
+        if (activity == null) {
+            activity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        }
+
         // Home stack should not be destroyed immediately.
         final ActivityRecord activity1 = finishCurrentActivity(homeStack);
         assertEquals(FINISHING, activity1.getState());
@@ -1068,7 +1102,7 @@
     public void testStackOrderChangedOnPositionStack() {
         StackOrderChangedListener listener = new StackOrderChangedListener();
         try {
-            final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+            final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                     mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
             mDefaultDisplay.registerStackOrderChangedListener(listener);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 3d94467..81fbfe4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,6 +79,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.server.wm.utils.MockTracker;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -186,6 +187,19 @@
         verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
     }
 
+    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+            int expectedResult) {
+        // We track mocks created here because this is used in a single test
+        // (testStartActivityPreconditions) as a specific case, and mocks created inside it won't be
+        // used for other cases. To avoid extensive memory usage, we clean up all used mocks after
+        // each case. This is necessary because usually we only clean up mocks after a test
+        // finishes, but this test creates too many mocks that the intermediate memory usage can be
+        // ~0.8 GiB and thus very susceptible to OutOfMemoryException.
+        try (MockTracker tracker = new MockTracker()) {
+            verifyStartActivityPreconditionsUntracked(preconditions, launchFlags, expectedResult);
+        }
+    }
+
     /**
      * Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
      * provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
@@ -197,7 +211,7 @@
      * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
      * @param expectedResult The expected result from the launch.
      */
-    private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+    private void verifyStartActivityPreconditionsUntracked(int preconditions, int launchFlags,
             int expectedResult) {
         final ActivityTaskManagerService service = mService;
         final IPackageManager packageManager = mock(IPackageManager.class);
@@ -329,9 +343,6 @@
                 any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
-        // Use factory that only returns spy task.
-        mockTaskRecordFactory();
-
         if (mockGetLaunchStack) {
             // Instrument the stack and task used.
             final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
@@ -482,7 +493,7 @@
     @Test
     public void testTaskModeViolation() {
         final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
-        ((TestActivityDisplay) display).removeAllTasks();
+        display.removeAllTasks();
         assertNoTasks(display);
 
         final ActivityStarter starter = prepareStarter(0);
@@ -676,18 +687,27 @@
         doReturn(isCallingUidDeviceOwner).when(mService).isDeviceOwner(callingUid);
 
         final ActivityOptions options = spy(ActivityOptions.makeBasic());
+        ActivityRecord[] outActivity = new ActivityRecord[1];
         ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
                 .setCallingPackage("com.whatever.dude")
                 .setCaller(caller)
                 .setCallingUid(callingUid)
                 .setRealCallingUid(realCallingUid)
-                .setActivityOptions(new SafeActivityOptions(options));
+                .setActivityOptions(new SafeActivityOptions(options))
+                .setOutActivity(outActivity);
 
         final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
 
         assertEquals(ActivityStarter.getExternalResult(
                 shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
         verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+
+        final ActivityRecord startedActivity = outActivity[0];
+        if (startedActivity != null && startedActivity.getTaskRecord() != null) {
+            // Remove the activity so it doesn't interfere with with subsequent activity launch
+            // tests from this method.
+            startedActivity.getTaskRecord().removeActivity(startedActivity);
+        }
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d8a9bb0..297aa7e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -45,8 +45,7 @@
     /** Verify that activity is finished correctly upon request. */
     @Test
     public void testActivityFinish() {
-        final TestActivityStack stack =
-                (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+        final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
         final ActivityRecord activity = stack.getChildAt(0).getTopActivity();
         assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
                 0 /* resultCode */, null /* resultData */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ab2da2b..a5dc241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -16,96 +16,58 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 
-import android.app.ActivityManagerInternal;
 import android.app.ActivityOptions;
-import android.app.AppOpsManager;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.testing.DexmakerShareClassLoaderRule;
-import android.view.Display;
 import android.view.DisplayInfo;
 
-import com.android.internal.app.IVoiceInteractor;
 import com.android.server.AttributeCache;
-import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.am.PendingIntentController;
-import com.android.server.appop.AppOpsService;
-import com.android.server.firewall.IntentFirewall;
-import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
-import com.android.server.wm.utils.MockTracker;
 
-import org.junit.After;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.io.File;
-import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * A base class to handle common operations in activity related unit tests.
  */
 class ActivityTestsBase {
-    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
 
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
 
+    @Rule
+    public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
+
     final Context mContext = getInstrumentation().getTargetContext();
-    final TestInjector mTestInjector = new TestInjector(mContext);
 
     ActivityTaskManagerService mService;
     RootActivityContainer mRootActivityContainer;
     ActivityStackSupervisor mSupervisor;
 
-    private MockTracker mMockTracker;
-
     // Default package name
     static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
 
@@ -119,37 +81,18 @@
 
     @Before
     public void setUpBase() {
-        mMockTracker = new MockTracker();
-
-        mTestInjector.setUp();
-
-        mService = new TestActivityTaskManagerService(mContext);
+        mService = mSystemServicesTestRule.getActivityTaskManagerService();
         mSupervisor = mService.mStackSupervisor;
         mRootActivityContainer = mService.mRootActivityContainer;
     }
 
-    @After
-    public void tearDownBase() {
-        mTestInjector.tearDown();
-        if (mService != null) {
-            mService.setWindowManager(null);
-            mService = null;
-        }
-        if (sMockWindowManagerService != null) {
-            reset(sMockWindowManagerService);
-        }
-
-        mMockTracker.close();
-        mMockTracker = null;
-    }
-
     /** Creates a {@link TestActivityDisplay}. */
     TestActivityDisplay createNewActivityDisplay() {
-        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+        return TestActivityDisplay.create(mSupervisor);
     }
 
     TestActivityDisplay createNewActivityDisplay(DisplayInfo info) {
-        return TestActivityDisplay.create(mSupervisor, sNextDisplayId++, info);
+        return TestActivityDisplay.create(mSupervisor, info);
     }
 
     /** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
@@ -166,25 +109,9 @@
         return display;
     }
 
-    /**
-     * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
-     * when starting activity in unit tests.
-     */
-    void mockTaskRecordFactory(Consumer<TaskBuilder> taskBuilderSetup) {
-        final TaskBuilder taskBuilder = new TaskBuilder(mSupervisor).setCreateStack(false);
-        if (taskBuilderSetup != null) {
-            taskBuilderSetup.accept(taskBuilder);
-        }
-        final TaskRecord task = taskBuilder.build();
-        final TaskRecordFactory factory = mock(TaskRecordFactory.class);
-        TaskRecord.setTaskRecordFactory(factory);
-        doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
-                any() /* info */, any() /* intent */, any() /* voiceSession */,
-                any() /* voiceInteractor */);
-    }
-
-    void mockTaskRecordFactory() {
-        mockTaskRecordFactory(null /* taskBuilderSetup */);
+    /** Sets the default minimum task size to 1 so that tests can use small task sizes */
+    public void removeGlobalMinSizeRestriction() {
+        mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
     }
 
     /**
@@ -204,6 +131,11 @@
         private ActivityStack mStack;
         private int mActivityFlags;
         private int mLaunchMode;
+        private int mResizeMode = RESIZE_MODE_RESIZEABLE;
+        private float mMaxAspectRatio;
+        private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+        private boolean mLaunchTaskBehind;
+        private int mConfigChanges;
 
         ActivityBuilder(ActivityTaskManagerService service) {
             mService = service;
@@ -254,6 +186,31 @@
             return this;
         }
 
+        ActivityBuilder setResizeMode(int resizeMode) {
+            mResizeMode = resizeMode;
+            return this;
+        }
+
+        ActivityBuilder setMaxAspectRatio(float maxAspectRatio) {
+            mMaxAspectRatio = maxAspectRatio;
+            return this;
+        }
+
+        ActivityBuilder setScreenOrientation(int screenOrientation) {
+            mScreenOrientation = screenOrientation;
+            return this;
+        }
+
+        ActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+            mLaunchTaskBehind = launchTaskBehind;
+            return this;
+        }
+
+        ActivityBuilder setConfigChanges(int configChanges) {
+            mConfigChanges = configChanges;
+            return this;
+        }
+
         ActivityRecord build() {
             if (mComponent == null) {
                 final int id = sCurrentActivityId++;
@@ -279,24 +236,31 @@
             }
             aInfo.flags |= mActivityFlags;
             aInfo.launchMode = mLaunchMode;
+            aInfo.resizeMode = mResizeMode;
+            aInfo.maxAspectRatio = mMaxAspectRatio;
+            aInfo.screenOrientation = mScreenOrientation;
+            aInfo.configChanges |= mConfigChanges;
+
+            ActivityOptions options = null;
+            if (mLaunchTaskBehind) {
+                options =  ActivityOptions.makeTaskLaunchBehind();
+            }
 
             final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
                     0 /* launchedFromPid */, 0, null, intent, null,
                     aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
                     0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
-                    mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+                    mService.mStackSupervisor, options, null /* sourceRecord */);
             spyOn(activity);
-            activity.mAppWindowToken = mock(AppWindowToken.class);
-            doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
-            doCallRealMethod().when(activity.mAppWindowToken)
-                    .setOrientation(anyInt(), any(), any());
-            doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
-            doNothing().when(activity).removeWindowContainer();
-            doReturn(mock(Configuration.class)).when(activity.mAppWindowToken)
-                    .getRequestedOverrideConfiguration();
-
             if (mTaskRecord != null) {
-                mTaskRecord.addActivityToTop(activity);
+                // fullscreen value is normally read from resources in ctor, so for testing we need
+                // to set it somewhere else since we can't mock resources.
+                activity.fullscreen = true;
+                activity.setTask(mTaskRecord);
+                activity.createAppWindowToken();
+                spyOn(activity.mAppWindowToken);
+                // Make visible by default...
+                activity.mAppWindowToken.setHidden(false);
             }
 
             final WindowProcessController wpc = new WindowProcessController(mService,
@@ -305,6 +269,9 @@
                     mock(WindowProcessListener.class));
             wpc.setThread(mock(IApplicationThread.class));
             activity.setProcess(wpc);
+
+            // Resume top activities to make sure all other signals in the system are connected.
+            mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
             return activity;
         }
     }
@@ -313,16 +280,13 @@
      * Builder for creating new tasks.
      */
     protected static class TaskBuilder {
-        // Default package name
-        static final String DEFAULT_PACKAGE = "com.bar";
-
         private final ActivityStackSupervisor mSupervisor;
 
         private ComponentName mComponent;
         private String mPackage;
         private int mFlags = 0;
         // Task id 0 is reserved in ARC for the home app.
-        private int mTaskId = 1;
+        private int mTaskId = SystemServicesTestRule.sNextTaskId++;
         private int mUserId = 0;
         private IVoiceInteractionSession mVoiceSession;
         private boolean mCreateStack = true;
@@ -381,6 +345,7 @@
             if (mStack == null && mCreateStack) {
                 mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+                spyOn(mStack);
             }
 
             final ActivityInfo aInfo = new ActivityInfo();
@@ -396,450 +361,22 @@
             intent.setComponent(mComponent);
             intent.setFlags(mFlags);
 
-            final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
+            final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
                     intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+            spyOn(task);
             task.userId = mUserId;
 
             if (mStack != null) {
                 mStack.moveToFront("test");
                 mStack.addTask(task, true, "creating test task");
-                task.setStack(mStack);
-                task.setTask();
-                mStack.getTaskStack().addChild(task.mTask, 0);
+                task.createTask(true, true);
+                spyOn(task.mTask);
             }
 
             task.touchActiveTime();
 
             return task;
         }
-
-        private static class TestTaskRecord extends TaskRecord {
-            TestTaskRecord(ActivityTaskManagerService service, int taskId, ActivityInfo info,
-                       Intent intent, IVoiceInteractionSession voiceSession,
-                       IVoiceInteractor voiceInteractor) {
-                super(service, taskId, info, intent, voiceSession, voiceInteractor);
-            }
-
-            @Override
-            void createTask(boolean onTop, boolean showForAllUsers) {
-                setTask();
-            }
-
-            void setTask() {
-                Task mockTask = mock(Task.class);
-                mockTask.mTaskRecord = this;
-                doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
-                setTask(mock(Task.class));
-            }
-        }
-    }
-
-    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
-        private PackageManagerInternal mPmInternal;
-        private PermissionPolicyInternal mPermissionPolicyInternal;
-
-        // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
-        // We keep the reference in order to prevent creating it twice.
-        ActivityStackSupervisor mTestStackSupervisor;
-
-        ActivityDisplay mDefaultDisplay;
-        AppOpsService mAppOpsService;
-
-        TestActivityTaskManagerService(Context context) {
-            super(context);
-            spyOn(this);
-
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-            mAppOpsService = mock(AppOpsService.class);
-
-            // Make sure permission checks aren't overridden.
-            doReturn(AppOpsManager.MODE_DEFAULT)
-                    .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
-
-            mSupportsMultiWindow = true;
-            mSupportsMultiDisplay = true;
-            mSupportsSplitScreenMultiWindow = true;
-            mSupportsFreeformWindowManagement = true;
-            mSupportsPictureInPicture = true;
-
-            final TestActivityManagerService am =
-                    new TestActivityManagerService(mTestInjector, this);
-
-            spyOn(getLifecycleManager());
-            spyOn(getLockTaskController());
-            spyOn(getTaskChangeNotificationController());
-            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
-            // allow background activity starts by default
-            doReturn(true).when(this).isBackgroundActivityStartsEnabled();
-            doNothing().when(this).updateCpuStats();
-        }
-
-        void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
-                ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
-            mAmInternal = amInternal;
-            initialize(intentFirewall, intentController, looper);
-            initRootActivityContainerMocks(wm);
-            setWindowManager(wm);
-            createDefaultDisplay();
-        }
-
-        void initRootActivityContainerMocks(WindowManagerService wm) {
-            spyOn(mRootActivityContainer);
-            mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
-            mRootActivityContainer.mWindowManager = wm;
-            mRootActivityContainer.mDisplayManager =
-                    (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
-            doNothing().when(mRootActivityContainer).setWindowManager(any());
-            // Invoked during {@link ActivityStack} creation.
-            doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
-            // Always keep things awake.
-            doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
-            // Called when moving activity to pinned stack.
-            doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
-                    anyBoolean());
-        }
-
-        void createDefaultDisplay() {
-            // Create a default display and put a home stack on it so that we'll always have
-            // something focusable.
-            mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
-            spyOn(mDefaultDisplay);
-            mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
-            mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
-            final TaskRecord task = new TaskBuilder(mStackSupervisor)
-                    .setStack(mDefaultDisplay.getHomeStack()).build();
-            new ActivityBuilder(this).setTask(task).build();
-
-            doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
-        }
-
-        @Override
-        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
-            return userId;
-        }
-
-        @Override
-        AppOpsService getAppOpsService() {
-            return mAppOpsService;
-        }
-
-        @Override
-        void updateCpuStats() {
-        }
-
-        @Override
-        void updateBatteryStats(ActivityRecord component, boolean resumed) {
-        }
-
-        @Override
-        void updateActivityUsageStats(ActivityRecord activity, int event) {
-        }
-
-        @Override
-        protected ActivityStackSupervisor createStackSupervisor() {
-            if (mTestStackSupervisor == null) {
-                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
-            }
-            return mTestStackSupervisor;
-        }
-
-        @Override
-        PackageManagerInternal getPackageManagerInternalLocked() {
-            if (mPmInternal == null) {
-                mPmInternal = mock(PackageManagerInternal.class);
-                doReturn(false)
-                        .when(mPmInternal)
-                        .isPermissionsReviewRequired(anyString(), anyInt());
-            }
-            return mPmInternal;
-        }
-
-        @Override
-        PermissionPolicyInternal getPermissionPolicyInternal() {
-            if (mPermissionPolicyInternal == null) {
-                mPermissionPolicyInternal = mock(PermissionPolicyInternal.class);
-                doReturn(true).when(mPermissionPolicyInternal).checkStartActivity(any(), anyInt(),
-                        any());
-            }
-            return mPermissionPolicyInternal;
-        }
-    }
-
-    private static class TestInjector extends ActivityManagerService.Injector {
-        private ServiceThread mHandlerThread;
-
-        TestInjector(Context context) {
-            super(context);
-        }
-
-        @Override
-        public AppOpsService getAppOpsService(File file, Handler handler) {
-            return null;
-        }
-
-        @Override
-        public Handler getUiHandler(ActivityManagerService service) {
-            return mHandlerThread.getThreadHandler();
-        }
-
-        @Override
-        public boolean isNetworkRestrictedForUid(int uid) {
-            return false;
-        }
-
-        void setUp() {
-            mHandlerThread = new ServiceThread("ActivityTestsThread",
-                    Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
-            mHandlerThread.start();
-        }
-
-        void tearDown() {
-            // Make sure there are no running messages and then quit the thread so the next test
-            // won't be affected.
-            mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
-                    0 /* timeout */);
-        }
-    }
-
-    // TODO: Replace this with a mock object since we are no longer in AMS package.
-    /**
-     * An {@link ActivityManagerService} subclass which provides a test
-     * {@link ActivityStackSupervisor}.
-     */
-    class TestActivityManagerService extends ActivityManagerService {
-
-        TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
-            super(testInjector, testInjector.mHandlerThread);
-            spyOn(this);
-
-            mWindowManager = prepareMockWindowManager();
-            mUgmInternal = mock(UriGrantsManagerInternal.class);
-
-            atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
-                    testInjector.mHandlerThread.getLooper());
-
-            mActivityTaskManager = atm;
-            mAtmInternal = atm.mInternal;
-
-            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
-            PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
-            doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
-            doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
-            doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
-        }
-    }
-
-    /**
-     * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
-     * setup not available in the test environment. Also specifies an injector for
-     */
-    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
-        private KeyguardController mKeyguardController;
-
-        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-            super(service, looper);
-            spyOn(this);
-            mWindowManager = prepareMockWindowManager();
-            mKeyguardController = mock(KeyguardController.class);
-
-            // Do not schedule idle that may touch methods outside the scope of the test.
-            doNothing().when(this).scheduleIdleLocked();
-            doNothing().when(this).scheduleIdleTimeoutLocked(any());
-            // unit test version does not handle launch wake lock
-            doNothing().when(this).acquireLaunchWakelock();
-            doReturn(mKeyguardController).when(this).getKeyguardController();
-
-            mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
-
-            initialize();
-        }
-
-        @Override
-        public KeyguardController getKeyguardController() {
-            return mKeyguardController;
-        }
-
-        @Override
-        void setWindowManager(WindowManagerService wm) {
-            mWindowManager = wm;
-        }
-    }
-
-    protected static class TestActivityDisplay extends ActivityDisplay {
-        private final ActivityStackSupervisor mSupervisor;
-
-        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
-            return create(supervisor, displayId, new DisplayInfo());
-        }
-
-        static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
-                DisplayInfo info) {
-            if (displayId == DEFAULT_DISPLAY) {
-                return new TestActivityDisplay(supervisor,
-                        supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
-            }
-            final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                    info, DEFAULT_DISPLAY_ADJUSTMENTS);
-            return new TestActivityDisplay(supervisor, display);
-        }
-
-        TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
-            super(supervisor.mService.mRootActivityContainer, display);
-            // Normally this comes from display-properties as exposed by WM. Without that, just
-            // hard-code to FULLSCREEN for tests.
-            setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            mSupervisor = supervisor;
-        }
-
-        @SuppressWarnings("TypeParameterUnusedInFormals")
-        @Override
-        ActivityStack createStackUnchecked(int windowingMode, int activityType,
-                int stackId, boolean onTop) {
-            return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
-                    .setWindowingMode(windowingMode).setActivityType(activityType)
-                    .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
-        }
-
-        @Override
-        protected DisplayContent createDisplayContent() {
-            final DisplayContent displayContent = mock(DisplayContent.class);
-            DockedStackDividerController divider = mock(DockedStackDividerController.class);
-            doReturn(divider).when(displayContent).getDockedDividerController();
-            return displayContent;
-        }
-
-        void removeAllTasks() {
-            for (int i = 0; i < getChildCount(); i++) {
-                final ActivityStack stack = getChildAt(i);
-                for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
-                    stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
-                }
-            }
-        }
-    }
-
-    private static WindowManagerService sMockWindowManagerService;
-
-    private static WindowManagerService prepareMockWindowManager() {
-        if (sMockWindowManagerService == null) {
-            sMockWindowManagerService = mock(WindowManagerService.class);
-        }
-
-        sMockWindowManagerService.mRoot = mock(RootWindowContainer.class);
-
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
-            if (runnable != null) {
-                runnable.run();
-            }
-            return null;
-        }).when(sMockWindowManagerService).inSurfaceTransaction(any());
-
-        return sMockWindowManagerService;
-    }
-
-    /**
-     * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
-     * method is called. Note that its functionality depends on the implementations of the
-     * construction arguments.
-     */
-    protected static class TestActivityStack
-            extends ActivityStack {
-        private int mOnActivityRemovedFromStackCount = 0;
-
-        static final int IS_TRANSLUCENT_UNSET = 0;
-        static final int IS_TRANSLUCENT_FALSE = 1;
-        static final int IS_TRANSLUCENT_TRUE = 2;
-        private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
-
-        static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
-        static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
-        static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
-        private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
-
-        TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
-                int windowingMode, int activityType, boolean onTop, boolean createActivity) {
-            super(display, stackId, supervisor, windowingMode, activityType, onTop);
-            if (createActivity) {
-                new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
-                if (onTop) {
-                    // We move the task to front again in order to regain focus after activity
-                    // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
-                    // could be other stacks (e.g. home stack).
-                    moveToFront("createActivityStack");
-                } else {
-                    moveToBack("createActivityStack", null);
-                }
-            }
-        }
-
-        @Override
-        void onActivityRemovedFromStack(ActivityRecord r) {
-            mOnActivityRemovedFromStackCount++;
-            super.onActivityRemovedFromStack(r);
-        }
-
-        // Returns the number of times {@link #onActivityRemovedFromStack} has been called
-        int onActivityRemovedFromStackInvocationCount() {
-            return mOnActivityRemovedFromStackCount;
-        }
-
-        @Override
-        protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
-            mTaskStack = mock(TaskStack.class);
-
-            // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
-            // will be moved to the full screen stack.
-            if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                outBounds.set(0, 0, 100, 100);
-            }
-        }
-
-        @Override
-        TaskStack getTaskStack() {
-            return mTaskStack;
-        }
-
-        void setIsTranslucent(boolean isTranslucent) {
-            mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
-        }
-
-        @Override
-        boolean isStackTranslucent(ActivityRecord starting) {
-            switch (mIsTranslucent) {
-                case IS_TRANSLUCENT_TRUE:
-                    return true;
-                case IS_TRANSLUCENT_FALSE:
-                    return false;
-                case IS_TRANSLUCENT_UNSET:
-                default:
-                    return super.isStackTranslucent(starting);
-            }
-        }
-
-        void setSupportsSplitScreen(boolean supportsSplitScreen) {
-            mSupportsSplitScreen = supportsSplitScreen
-                    ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
-        }
-
-        @Override
-        public boolean supportsSplitScreenWindowingMode() {
-            switch (mSupportsSplitScreen) {
-                case SUPPORTS_SPLIT_SCREEN_TRUE:
-                    return true;
-                case SUPPORTS_SPLIT_SCREEN_FALSE:
-                    return false;
-                case SUPPORTS_SPLIT_SCREEN_UNSET:
-                default:
-                    return super.supportsSplitScreenWindowingMode();
-            }
-        }
-
-        @Override
-        void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
-                                 boolean newTask, boolean keepCurTransition,
-                                 ActivityOptions options) {
-        }
     }
 
     static class StackBuilder {
@@ -886,27 +423,45 @@
             return this;
         }
 
-        @SuppressWarnings("TypeParameterUnusedInFormals")
         ActivityStack build() {
             final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+            final ActivityStack stack;
+            final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor;
             if (mWindowingMode == WINDOWING_MODE_PINNED) {
-                return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor,
+                stack = new ActivityStack(mDisplay, stackId, supervisor,
                         mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
                     @Override
                     Rect getDefaultPictureInPictureBounds(float aspectRatio) {
                         return new Rect(50, 50, 100, 100);
                     }
-
-                    @Override
-                    void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
-                        mTaskStack = mock(TaskStack.class);
-                    }
                 };
             } else {
-                return new TestActivityStack(mDisplay, stackId,
-                        mRootActivityContainer.mStackSupervisor, mWindowingMode,
-                        mActivityType, mOnTop, mCreateActivity);
+                stack = new ActivityStack(mDisplay, stackId, supervisor,
+                        mWindowingMode, mActivityType, mOnTop);
+
+                if (mCreateActivity) {
+                    new ActivityBuilder(supervisor.mService)
+                            .setCreateTask(true)
+                            .setStack(stack)
+                            .build();
+                    if (mOnTop) {
+                        // We move the task to front again in order to regain focus after activity
+                        // added to the stack.
+                        // Or {@link ActivityDisplay#mPreferredTopFocusableStack} could be other
+                        // stacks (e.g. home stack).
+                        stack.moveToFront("createActivityStack");
+                    } else {
+                        stack.moveToBack("createActivityStack", null);
+                    }
+                }
             }
+
+            spyOn(stack);
+            spyOn(stack.mTaskStack);
+            doNothing().when(stack).startActivityLocked(
+                    any(), any(), anyBoolean(), anyBoolean(), any());
+
+            return stack;
         }
 
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 20379a2..e71c8f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -60,7 +60,7 @@
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentOpening.setFillsParent(false);
+            translucentOpening.setOccludesParent(false);
             translucentOpening.setHidden(true);
             mDisplayContent.mOpeningApps.add(behind);
             mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -78,7 +78,7 @@
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentClosing.setFillsParent(false);
+            translucentClosing.setOccludesParent(false);
             mDisplayContent.mClosingApps.add(translucentClosing);
             assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
                     mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
@@ -94,7 +94,7 @@
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
             final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
-            translucentOpening.setFillsParent(false);
+            translucentOpening.setOccludesParent(false);
             translucentOpening.setHidden(true);
             mDisplayContent.mOpeningApps.add(behind);
             mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -110,10 +110,10 @@
         synchronized (mWm.mGlobalLock) {
             final AppWindowToken opening = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-            opening.setFillsParent(false);
+            opening.setOccludesParent(false);
             final AppWindowToken closing = createAppWindowToken(mDisplayContent,
                     WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-            closing.setFillsParent(false);
+            closing.setOccludesParent(false);
             Task task = opening.getTask();
             mDisplayContent.mOpeningApps.add(opening);
             mDisplayContent.mClosingApps.add(closing);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index d1dc382..c162b6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -30,12 +30,14 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 
 import android.graphics.Rect;
 import android.os.IBinder;
@@ -195,6 +197,7 @@
     @Test
     public void testCancelRemoteAnimationWhenFreeze() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
         final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
                 dc, "exiting app");
         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d9566a3..e387e18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -36,7 +36,7 @@
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -152,10 +152,6 @@
     @Test
     @FlakyTest(bugId = 131005232)
     public void testLandscapeSeascapeRotationByApp() {
-        // Some plumbing to get the service ready for rotation updates.
-        mWm.mDisplayReady = true;
-        mWm.mDisplayEnabled = true;
-
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
         attrs.setTitle("AppWindow");
@@ -185,25 +181,21 @@
 
     @Test
     public void testLandscapeSeascapeRotationByPolicy() {
-        // Some plumbing to get the service ready for rotation updates.
-        mWm.mDisplayReady = true;
-        mWm.mDisplayEnabled = true;
-
-        final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
-        mDisplayContent.setDisplayRotation(spiedRotation);
+        final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+        spyOn(displayRotation);
 
         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
                 TYPE_BASE_APPLICATION);
-        attrs.setTitle("AppWindow");
+        attrs.setTitle("RotationByPolicy");
         final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
         mToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        performRotation(spiedRotation, Surface.ROTATION_90);
+        performRotation(displayRotation, Surface.ROTATION_90);
         appWindow.mResizeReported = false;
 
         // Update the rotation to perform 180 degree rotation and check that resize was reported.
-        performRotation(spiedRotation, Surface.ROTATION_270);
+        performRotation(displayRotation, Surface.ROTATION_270);
         assertTrue(appWindow.mResizeReported);
 
         appWindow.removeImmediately();
@@ -211,14 +203,7 @@
 
     private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
         doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
-        int oldRotation = mDisplayContent.getRotation();
         mWm.updateRotation(false, false);
-        // Must manually apply here since ATM doesn't know about the display during this test
-        // (meaning it can't perform the normal sendNewConfiguration flow).
-        mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation());
-        // Prevent the next rotation from being deferred by animation.
-        mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
-        mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
     }
 
     @Test
@@ -266,14 +251,14 @@
     public void testGetOrientation() {
         mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
 
-        mToken.setFillsParent(false);
-        // Can specify orientation if app doesn't fill parent.
+        mToken.setOccludesParent(false);
+        // Can specify orientation if app doesn't occludes parent.
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
 
-        mToken.setFillsParent(true);
+        mToken.setOccludesParent(true);
         mToken.setHidden(true);
         mToken.sendingToBottom = true;
-        // Can not specify orientation if app isn't visible even though it fills parent.
+        // Can not specify orientation if app isn't visible even though it occludes parent.
         assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
         // Can specify orientation if the current orientation candidate is orientation behind.
         assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 388658d..6289768 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,7 +83,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.wm.utils.WmDisplayCutout;
@@ -653,25 +652,27 @@
     @Test
     public void testOnDescendantOrientationRequestChanged() {
         final DisplayContent dc = createNewDisplay();
-        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+        dc.getDisplayRotation().setFixedToUserRotation(
+                DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
                 : SCREEN_ORIENTATION_LANDSCAPE;
 
-        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
-        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        final ActivityStack stack =
+                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+                        .setDisplay(dc.mAcitvityDisplay).build();
+        final ActivityRecord activity = stack.topTask().getTopActivity();
 
-        ActivityRecord activityRecord = mock(ActivityRecord.class);
-
-        assertTrue("Display should rotate to handle orientation request by default.",
-                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+        activity.setRequestedOrientation(newOrientation);
 
         final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
         verify(dc.mAcitvityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
-                same(activityRecord), anyBoolean(), same(null));
+                same(activity), anyBoolean(), same(null));
         final Configuration newDisplayConfig = captor.getValue();
-        assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+        final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
+                ? Configuration.ORIENTATION_PORTRAIT
+                : Configuration.ORIENTATION_LANDSCAPE;
+        assertEquals(expectedOrientation, newDisplayConfig.orientation);
     }
 
     @Test
@@ -679,22 +680,20 @@
         final DisplayContent dc = createNewDisplay();
         dc.getDisplayRotation().setFixedToUserRotation(
                 DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
-        mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
                 ? SCREEN_ORIENTATION_PORTRAIT
                 : SCREEN_ORIENTATION_LANDSCAPE;
 
-        final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
-        window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
-        window.mAppToken.setOrientation(newOrientation);
+        final ActivityStack stack =
+                new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+                        .setDisplay(dc.mAcitvityDisplay).build();
+        final ActivityRecord activity = stack.topTask().getTopActivity();
 
-        ActivityRecord activityRecord = mock(ActivityRecord.class);
+        activity.setRequestedOrientation(newOrientation);
 
-        assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
-                        + " user rotation.",
-                dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
         verify(dc.mAcitvityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
-                eq(activityRecord), anyBoolean(), same(null));
+                eq(activity), anyBoolean(), same(null));
+        assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index bfede51..f6f8811 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -95,7 +95,7 @@
     public void setUp() throws Exception {
         deleteRecursively(TEST_FOLDER);
 
-        mWm.setSupportsFreeformWindowManagement(false);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = false;
         mWm.setIsPc(false);
         mWm.setForceDesktopModeOnExternalDisplays(false);
 
@@ -134,7 +134,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
@@ -144,7 +144,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_HasDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setForceDesktopModeOnExternalDisplays(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -155,7 +155,7 @@
 
     @Test
     public void testPrimaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -168,7 +168,7 @@
     public void testPrimaryDisplayUpdateToFreeform_HasFreeformSupport_IsPc() {
         mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
 
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setIsPc(true);
 
         mTarget.updateSettingsForDisplay(mPrimaryDisplay);
@@ -187,7 +187,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_NoDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
 
@@ -197,7 +197,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_HasDesktopMode() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setForceDesktopModeOnExternalDisplays(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
@@ -208,7 +208,7 @@
 
     @Test
     public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
-        mWm.setSupportsFreeformWindowManagement(true);
+        mWm.mAtmService.mSupportsFreeformWindowManagement = true;
         mWm.setIsPc(true);
 
         mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index b28ae40..be2ee29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -331,7 +331,9 @@
 
         mController.layoutTask(task, null /* windowLayout */);
 
-        assertEquals(expected, task.getBounds());
+        // TaskRecord will make adjustments to requested bounds. We only need to guarantee that the
+        // reuqested bounds are expected.
+        assertEquals(expected, task.getRequestedOverrideBounds());
     }
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index e4d3770..49d778f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -104,6 +104,7 @@
 
         mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
         final DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
         info.uniqueId = mDisplayUniqueId;
         mTestDisplay = createNewActivityDisplay(info);
         mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 63d9fb9..efd468f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -61,7 +61,7 @@
 
     @Test
     public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
-        mWm.mSupportsPictureInPicture = true;
+        mWm.mAtmService.mSupportsPictureInPicture = true;
         mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
 
         verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b7a85d7..fb4e330 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -32,7 +32,7 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -53,14 +53,12 @@
 import android.app.ActivityTaskManager;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
@@ -93,11 +91,8 @@
     private static final int TEST_QUIET_USER_ID = 20;
     private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
     private static final UserInfo QUIET_USER_INFO = new UserInfo();
-    private static int sLastTaskId = 1;
-    private static int sLastStackId = 1;
     private static final int INVALID_STACK_ID = 999;
 
-    private TestActivityTaskManagerService mTestService;
     private ActivityDisplay mDisplay;
     private ActivityDisplay mOtherDisplay;
     private ActivityDisplay mSingleTaskDisplay;
@@ -115,13 +110,29 @@
     @Before
     public void setUp() throws Exception {
         mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
-        mTestService = new MyTestActivityTaskManagerService(mContext);
-        mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
+
+        // Set testing displays
+        mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+        mOtherDisplay = createNewActivityDisplay();
+        mSingleTaskDisplay = createNewActivityDisplay();
+        mSingleTaskDisplay.setDisplayToSingleTaskInstance();
+        mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+        mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+        mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
+
+        // Set the recent tasks we should use for testing in this class.
+        mRecentTasks = new TestRecentTasks(mService, mTaskPersister);
+        spyOn(mRecentTasks);
+        mService.setRecentTasks(mRecentTasks);
         mRecentTasks.loadParametersFromResources(mContext.getResources());
-        mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
-        mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
+
+        // Set the running tasks we should use for testing in this class.
+        mRunningTasks = new TestRunningTasks();
+        mService.mStackSupervisor.setRunningTasks(mRunningTasks);
+
+        mHomeStack = mDisplay.getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
-        mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
+        mStack = mDisplay.createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         mCallbacksRecorder = new CallbacksRecorder();
         mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -723,7 +734,7 @@
 
         ActivityStack stack = mTasks.get(2).getStack();
         stack.moveToFront("", mTasks.get(2));
-        doReturn(stack).when(mTestService.mRootActivityContainer).getTopDisplayFocusedStack();
+        doReturn(stack).when(mService.mRootActivityContainer).getTopDisplayFocusedStack();
 
         // Simulate the reset from the timeout
         mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
@@ -742,10 +753,9 @@
     public void testBackStackTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
         final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack aboveHomeStack = mDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
         // the tasks belong in stacks above the home stack
@@ -761,11 +771,11 @@
     public void testBehindHomeStackTasks_expectTaskTrimmed() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
-        final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack behindHomeStack = mDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack aboveHomeStack = mDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
         // the home stack is trimmed once a new task is added
@@ -783,10 +793,9 @@
     public void testOtherDisplayTasks_expectNoTrim() {
         mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
 
-        final MyTestActivityStackSupervisor supervisor =
-                (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
         final ActivityStack homeStack = mDisplay.getHomeStack();
-        final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
+        final ActivityStack otherDisplayStack = mOtherDisplay.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
@@ -887,16 +896,16 @@
         mStack.remove();
 
         // The following APIs should not restore task from recents to the active list.
-        assertNotRestoreTask(() -> mTestService.setFocusedTask(taskId));
-        assertNotRestoreTask(() -> mTestService.startSystemLockTaskMode(taskId));
-        assertNotRestoreTask(() -> mTestService.cancelTaskWindowTransition(taskId));
+        assertNotRestoreTask(() -> mService.setFocusedTask(taskId));
+        assertNotRestoreTask(() -> mService.startSystemLockTaskMode(taskId));
+        assertNotRestoreTask(() -> mService.cancelTaskWindowTransition(taskId));
         assertNotRestoreTask(
-                () -> mTestService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
+                () -> mService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
         assertNotRestoreTask(
-                () -> mTestService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
+                () -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
                         false/* toTop */));
         assertNotRestoreTask(
-                () -> mTestService.setTaskWindowingModeSplitScreenPrimary(taskId,
+                () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId,
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
                         false /* toTop */, false /* animate */, null /* initialBounds */,
                         true /* showRecents */));
@@ -910,7 +919,7 @@
         mRecentTasks.remove(task);
 
         TaskChangeNotificationController controller =
-                mTestService.getTaskChangeNotificationController();
+                mService.getTaskChangeNotificationController();
         verify(controller, times(2)).notifyTaskListUpdated();
     }
 
@@ -923,7 +932,7 @@
 
         // 2 calls - Once for add and once for remove
         TaskChangeNotificationController controller =
-                mTestService.getTaskChangeNotificationController();
+                mService.getTaskChangeNotificationController();
         verify(controller, times(2)).notifyTaskListUpdated();
     }
 
@@ -938,7 +947,7 @@
 
         // 4 calls - Twice for add and twice for remove
         TaskChangeNotificationController controller =
-                mTestService.getTaskChangeNotificationController();
+                mService.getTaskChangeNotificationController();
         verify(controller, times(4)).notifyTaskListUpdated();
     }
 
@@ -980,7 +989,7 @@
 
     @Test
     public void testNotRecentsComponent_denyApiAccess() throws Exception {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
                 .checkGetTasksPermission(anyString(), anyInt(), anyInt());
         // Expect the following methods to fail due to recents component not being set
         mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
@@ -992,7 +1001,7 @@
 
     @Test
     public void testRecentsComponent_allowApiAccessWithoutPermissions() {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+        doReturn(PackageManager.PERMISSION_DENIED).when(mService)
                 .checkGetTasksPermission(anyString(), anyInt(), anyInt());
 
         // Set the recents component and ensure that the following calls do not fail
@@ -1002,62 +1011,62 @@
     }
 
     private void doTestRecentTasksApis(boolean expectCallable) {
-        assertSecurityException(expectCallable, () -> mTestService.removeStack(INVALID_STACK_ID));
+        assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
         assertSecurityException(expectCallable,
-                () -> mTestService.removeStacksInWindowingModes(
+                () -> mService.removeStacksInWindowingModes(
                         new int[]{WINDOWING_MODE_UNDEFINED}));
         assertSecurityException(expectCallable,
-                () -> mTestService.removeStacksWithActivityTypes(
+                () -> mService.removeStacksWithActivityTypes(
                         new int[]{ACTIVITY_TYPE_UNDEFINED}));
-        assertSecurityException(expectCallable, () -> mTestService.removeTask(0));
+        assertSecurityException(expectCallable, () -> mService.removeTask(0));
         assertSecurityException(expectCallable,
-                () -> mTestService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
+                () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
         assertSecurityException(expectCallable,
-                () -> mTestService.moveTaskToStack(0, INVALID_STACK_ID, true));
+                () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
-                () -> mTestService.setTaskWindowingModeSplitScreenPrimary(0,
+                () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
-        assertSecurityException(expectCallable, () -> mTestService.dismissSplitScreenMode(true));
-        assertSecurityException(expectCallable, () -> mTestService.dismissPip(true, 0));
+        assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+        assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
         assertSecurityException(expectCallable,
-                () -> mTestService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+                () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable,
-                () -> mTestService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+                () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
         assertSecurityException(expectCallable,
-                () -> mTestService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+                () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
                         new Rect()));
         assertSecurityException(expectCallable,
-                () -> mTestService.resizePinnedStack(new Rect(), new Rect()));
-        assertSecurityException(expectCallable, () -> mTestService.getAllStackInfos());
+                () -> mService.resizePinnedStack(new Rect(), new Rect()));
+        assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
         assertSecurityException(expectCallable,
-                () -> mTestService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+                () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
         assertSecurityException(expectCallable, () -> {
             try {
-                mTestService.getFocusedStackInfo();
+                mService.getFocusedStackInfo();
             } catch (RemoteException e) {
                 // Ignore
             }
         });
         assertSecurityException(expectCallable,
-                () -> mTestService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+                () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
-                () -> mTestService.startActivityFromRecents(0, new Bundle()));
-        assertSecurityException(expectCallable, () -> mTestService.getTaskSnapshot(0, true));
-        assertSecurityException(expectCallable, () -> mTestService.registerTaskStackListener(null));
+                () -> mService.startActivityFromRecents(0, new Bundle()));
+        assertSecurityException(expectCallable, () -> mService.getTaskSnapshot(0, true));
+        assertSecurityException(expectCallable, () -> mService.registerTaskStackListener(null));
         assertSecurityException(expectCallable,
-                () -> mTestService.unregisterTaskStackListener(null));
-        assertSecurityException(expectCallable, () -> mTestService.getTaskDescription(0));
-        assertSecurityException(expectCallable, () -> mTestService.cancelTaskWindowTransition(0));
-        assertSecurityException(expectCallable, () -> mTestService.startRecentsActivity(null, null,
+                () -> mService.unregisterTaskStackListener(null));
+        assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+        assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+        assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
                 null));
-        assertSecurityException(expectCallable, () -> mTestService.cancelRecentsAnimation(true));
-        assertSecurityException(expectCallable, () -> mTestService.stopAppSwitches());
-        assertSecurityException(expectCallable, () -> mTestService.resumeAppSwitches());
+        assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
+        assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
+        assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
     }
 
     private void testGetTasksApis(boolean expectCallable) {
-        mTestService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
-        mTestService.getTasks(MAX_VALUE);
+        mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
+        mService.getTasks(MAX_VALUE);
         if (expectCallable) {
             assertTrue(mRecentTasks.mLastAllowed);
             assertTrue(mRunningTasks.mLastAllowed);
@@ -1072,10 +1081,9 @@
     }
 
     private TaskBuilder createTaskBuilder(String packageName, String className) {
-        return new TaskBuilder(mTestService.mStackSupervisor)
+        return new TaskBuilder(mService.mStackSupervisor)
                 .setComponent(new ComponentName(packageName, className))
                 .setStack(mStack)
-                .setTaskId(sLastTaskId++)
                 .setUserId(TEST_USER_0_ID);
     }
 
@@ -1140,68 +1148,6 @@
         }
     }
 
-    private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
-        MyTestActivityTaskManagerService(Context context) {
-            super(context);
-        }
-
-        @Override
-        protected RecentTasks createRecentTasks() {
-            return spy(new TestRecentTasks(this, mTaskPersister));
-        }
-
-        @Override
-        protected ActivityStackSupervisor createStackSupervisor() {
-            if (mTestStackSupervisor == null) {
-                mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
-            }
-            return mTestStackSupervisor;
-        }
-
-        @Override
-        void createDefaultDisplay() {
-            super.createDefaultDisplay();
-            mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
-            mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
-            mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor,
-                    DEFAULT_DISPLAY + 2);
-            mSingleTaskDisplay.setDisplayToSingleTaskInstance();
-            mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
-            mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
-            mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
-        }
-    }
-
-    private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
-        MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
-            super(service, looper);
-        }
-
-        @Override
-        RunningTasks createRunningTasks() {
-            mRunningTasks = new TestRunningTasks();
-            return mRunningTasks;
-        }
-    }
-
-    private static class MyTestActivityStack extends TestActivityStack {
-        private ActivityDisplay mDisplay = null;
-
-        MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
-            super(display, sLastStackId++, supervisor, WINDOWING_MODE_FULLSCREEN,
-                    ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
-            mDisplay = display;
-        }
-
-        @Override
-        ActivityDisplay getDisplay() {
-            if (mDisplay != null) {
-                return mDisplay;
-            }
-            return super.getDisplay();
-        }
-    }
-
     private static class CallbacksRecorder implements Callbacks {
         public final ArrayList<TaskRecord> mAdded = new ArrayList<>();
         public final ArrayList<TaskRecord> mTrimmed = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index f5a1d75..9ca0180 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -30,6 +30,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
 
@@ -132,8 +133,14 @@
     @Test
     public void testIncludedApps_expectTargetAndVisible() {
         mWm.setRecentsAnimationController(mController);
-        final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        final ActivityStack homStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+        final AppWindowToken homeAppWindow =
+                new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+                        .setStack(homStack)
+                        .setCreateTask(true)
+                        .build()
+                        .mAppWindowToken;
         final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
         final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
@@ -169,7 +176,7 @@
 
         // Simulate the app transition finishing
         mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
     }
 
     @Test
@@ -201,7 +208,7 @@
         spyOn(mController.mRecentScreenshotAnimator.mAnimatable);
         mController.mRecentScreenshotAnimator.cancelAnimation();
         verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any());
-        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+        verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 0e119e3..dcc295c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -74,8 +74,9 @@
     @Before
     public void setUp() throws Exception {
         mRecentsAnimationController = mock(RecentsAnimationController.class);
-        doReturn(mRecentsAnimationController).when(
-                mService.mWindowManager).getRecentsAnimationController();
+        mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
+        doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
+                anyInt(), any(), any(), anyInt(), any());
         doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
 
         final RecentTasks recentTasks = mService.getRecentTasks();
@@ -107,16 +108,25 @@
         assertTrue(recentActivity.visible);
 
         // Simulate the animation is cancelled without changing the stack order.
-        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
-                false /* sendUserLeaveHint */);
+        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
         // The non-top recents activity should be invisible by the restored launch-behind state.
         assertFalse(recentActivity.visible);
     }
 
     @Test
     public void testPreloadRecentsActivity() {
-        // Ensure that the fake recent component can be resolved by the recents intent.
-        mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent));
+        final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+        final ActivityStack homeStack =
+                defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        defaultDisplay.positionChildAtTop(homeStack, false /* includingParents */);
+        ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+        if (topRunningHomeActivity == null) {
+            topRunningHomeActivity = new ActivityBuilder(mService)
+                    .setStack(homeStack)
+                    .setCreateTask(true)
+                    .build();
+        }
+
         ActivityInfo aInfo = new ActivityInfo();
         aInfo.applicationInfo = new ApplicationInfo();
         aInfo.applicationInfo.uid = 10001;
@@ -204,6 +214,13 @@
         ActivityStack homeStack = display.getHomeStack();
         // Assume the home activity support recents.
         ActivityRecord targetActivity = homeStack.getTopActivity();
+        if (targetActivity == null) {
+            targetActivity = new ActivityBuilder(mService)
+                    .setCreateTask(true)
+                    .setStack(homeStack)
+                    .build();
+        }
+
         // Put another home activity in home stack.
         ActivityRecord anotherHomeActivity = new ActivityBuilder(mService)
                 .setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
@@ -226,13 +243,12 @@
 
         anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome");
         // The current top activity is not the recents so the animation should be canceled.
-        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
                 eq(REORDER_KEEP_IN_PLACE), any() /* reason */);
 
         // The test uses mocked RecentsAnimationController so we have to invoke the callback
         // manually to simulate the flow.
-        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
-                false /* sendUserLeaveHint */);
+        recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
         // We should restore the launch-behind of the original target activity.
         assertFalse(targetActivity.mLaunchTaskBehind);
     }
@@ -269,7 +285,7 @@
         fullscreenStack.moveToFront("Activity start");
 
         // Ensure that the recents animation was canceled by cancelAnimationSynchronously().
-        verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
                 eq(REORDER_KEEP_IN_PLACE), any());
 
         // Assume recents animation already started, set a state that cancel recents animation
@@ -314,7 +330,7 @@
         fullscreenStack.remove();
 
         // Ensure that the recents animation was NOT canceled
-        verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
+        verify(mService.mWindowManager, times(0)).cancelRecentsAnimation(
                 eq(REORDER_KEEP_IN_PLACE), any());
         verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart();
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index d4f24f9..539a79c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -97,7 +97,7 @@
      */
     @Test
     public void testRestoringInvalidTask() {
-        ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+        mRootActivityContainer.getDefaultDisplay().removeAllTasks();
         TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
                 MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
         assertNull(task);
@@ -304,21 +304,23 @@
      */
     @Test
     public void testResizeDockedStackForSplitScreenPrimary() {
-        final Rect taskSize = new Rect(0, 0, 600, 600);
+        final Rect taskSize = new Rect(0, 0, 1000, 1000);
         final Rect stackSize = new Rect(0, 0, 300, 300);
 
         // Create primary split-screen stack with a task.
-        final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
-                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
-                        true /* onTop */);
-        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+        final ActivityStack primaryStack = new StackBuilder(mRootActivityContainer)
+                .setActivityType(ACTIVITY_TYPE_STANDARD)
+                .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+                .setOnTop(true)
+                .build();
+        final TaskRecord task = primaryStack.topTask();
 
         // Resize dock stack.
         mService.resizeDockedStack(stackSize, taskSize, null, null, null);
 
         // Verify dock stack & its task bounds if is equal as resized result.
-        assertEquals(primaryStack.getBounds(), stackSize);
-        assertEquals(task.getBounds(), taskSize);
+        assertEquals(stackSize, primaryStack.getBounds());
+        assertEquals(taskSize, task.getBounds());
     }
 
     /**
@@ -328,8 +330,9 @@
     public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
         // Create stack/task on default display.
         final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
-        final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
-                mRootActivityContainer).setOnTop(false).build();
+        final ActivityStack targetStack = new StackBuilder(mRootActivityContainer)
+                .setOnTop(false)
+                .build();
         final TaskRecord targetTask = targetStack.getChildAt(0);
 
         // Create Recents on top of the display.
@@ -505,12 +508,10 @@
         mockResolveSecondaryHomeActivity();
 
         // Create secondary displays.
-        final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+        final TestActivityDisplay secondDisplay = createNewActivityDisplay();
         mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
         doReturn(true).when(secondDisplay).supportsSystemDecorations();
 
-        // Create mock tasks and other necessary mocks.
-        mockTaskRecordFactory();
         doReturn(true).when(mRootActivityContainer)
                 .ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
         doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -621,7 +622,6 @@
         info.applicationInfo.packageName = "android";
         info.name = ResolverActivity.class.getName();
         doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
-        mockTaskRecordFactory();
 
         mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
         final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index f51ce13..db105dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -96,7 +96,7 @@
                     mWm.getDefaultDisplayContentLocked().getWindowingMode());
 
             mWm.mIsPc = true;
-            mWm.mSupportsFreeformWindowManagement = true;
+            mWm.mAtmService.mSupportsFreeformWindowManagement = true;
 
             mWm.mRoot.onSettingsRetrieved();
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index df7c9a4..1ad0e00 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -16,6 +16,9 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
 import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -25,67 +28,91 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManagerInternal;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
 import android.os.UserHandle;
-import android.view.Display;
+import android.provider.DeviceConfig;
 import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceControl;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.AnimationThread;
+import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
+import com.android.server.ServiceThread;
 import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.appop.AppOpsService;
+import com.android.server.display.color.ColorDisplayService;
 import com.android.server.input.InputManagerService;
+import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.utils.MockTracker;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
+import org.mockito.Mockito;
 import org.mockito.quality.Strictness;
 
+import java.io.File;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
- * JUnit test rule to create a mock {@link WindowManagerService} instance for tests.
+ * JUnit test rule to correctly setting up system services like {@link WindowManagerService}
+ * and {@link ActivityTaskManagerService} for tests.
  */
 public class SystemServicesTestRule implements TestRule {
 
     private static final String TAG = SystemServicesTestRule.class.getSimpleName();
 
+    static int sNextDisplayId = DEFAULT_DISPLAY + 100;
+    static int sNextTaskId = 100;
+
     private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false);
 
-    private MockTracker mMockTracker;
+    private Context mContext;
     private StaticMockitoSession mMockitoSession;
-    private WindowManagerService mWindowManagerService;
-    private TestWindowManagerPolicy mWindowManagerPolicy;
-
-    /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */
-    private static class Tracker extends MockTracker {
-        // This empty extended class is necessary since Mockito distinguishes a listener by it
-        // class.
-    }
+    ServiceThread mHandlerThread;
+    private ActivityManagerService mAmService;
+    private ActivityTaskManagerService mAtmService;
+    private WindowManagerService mWmService;
+    private TestWindowManagerPolicy mWMPolicy;
+    private WindowState.PowerManagerWrapper mPowerManagerWrapper;
+    private InputManagerService mImService;
+    /**
+     * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
+     */
+    SurfaceControl.Transaction mTransaction;
 
     @Override
     public Statement apply(Statement base, Description description) {
@@ -103,8 +130,6 @@
     }
 
     private void setUp() {
-        mMockTracker = new Tracker();
-
         mMockitoSession = mockitoSession()
                 .spyStatic(LocalServices.class)
                 .mockStatic(LockGuard.class)
@@ -112,101 +137,220 @@
                 .strictness(Strictness.LENIENT)
                 .startMocking();
 
+        setUpSystemCore();
+        setUpLocalServices();
+        setUpActivityTaskManagerService();
+        setUpWindowManagerService();
+    }
+
+    private void setUpSystemCore() {
+        mHandlerThread = new ServiceThread(
+                "WmTestsThread", THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+        mHandlerThread.start();
+
         doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
 
-        final Context context = getInstrumentation().getTargetContext();
-        spyOn(context);
+        mContext = getInstrumentation().getTargetContext();
+        spyOn(mContext);
 
-        doReturn(null).when(context)
+        doReturn(null).when(mContext)
                 .registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
-        doReturn(null).when(context)
+        doReturn(null).when(mContext)
                 .registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
                         any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
 
-        final ContentResolver contentResolver = context.getContentResolver();
+        final ContentResolver contentResolver = mContext.getContentResolver();
         spyOn(contentResolver);
         doNothing().when(contentResolver)
                 .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
                         anyInt());
+    }
 
-        final AppOpsManager appOpsManager = mock(AppOpsManager.class);
-        doReturn(appOpsManager).when(context)
-                .getSystemService(eq(Context.APP_OPS_SERVICE));
+    private void setUpLocalServices() {
+        // Tear down any local services just in case.
+        tearDownLocalServices();
 
+        // UriGrantsManagerInternal
+        final UriGrantsManagerInternal ugmi = mock(UriGrantsManagerInternal.class);
+        LocalServices.addService(UriGrantsManagerInternal.class, ugmi);
+
+        // AppOpsManager
+        final AppOpsManager aom = mock(AppOpsManager.class);
+        doReturn(aom).when(mContext).getSystemService(eq(Context.APP_OPS_SERVICE));
+
+        // DisplayManagerInternal
         final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
         doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
 
+        // ColorDisplayServiceInternal
+        final ColorDisplayService.ColorDisplayServiceInternal cds =
+                mock(ColorDisplayService.ColorDisplayServiceInternal.class);
+        doReturn(cds).when(() -> LocalServices.getService(
+                eq(ColorDisplayService.ColorDisplayServiceInternal.class)));
+
+        final UsageStatsManagerInternal usmi = mock(UsageStatsManagerInternal.class);
+        LocalServices.addService(UsageStatsManagerInternal.class, usmi);
+
+        // PackageManagerInternal
+        final PackageManagerInternal packageManagerInternal = mock(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, packageManagerInternal);
+        doReturn(false).when(packageManagerInternal).isPermissionsReviewRequired(
+                anyString(), anyInt());
+        doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt());
+
+        // PowerManagerInternal
         final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
         final PowerSaveState state = new PowerSaveState.Builder().build();
         doReturn(state).when(pmi).getLowPowerState(anyInt());
         doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class)));
 
-        final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
-        doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class)));
+        // PermissionPolicyInternal
+        final PermissionPolicyInternal ppi = mock(PermissionPolicyInternal.class);
+        LocalServices.addService(PermissionPolicyInternal.class, ppi);
+        doReturn(true).when(ppi).checkStartActivity(any(), anyInt(), any());
 
-        final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            final Runnable runnable = invocationOnMock.getArgument(0);
-            if (runnable != null) {
-                runnable.run();
-            }
-            return null;
-        }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
-        doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class)));
-
-        final InputManagerService ims = mock(InputManagerService.class);
+        // InputManagerService
+        mImService = mock(InputManagerService.class);
         // InputChannel is final and can't be mocked.
         final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
         if (input != null && input.length > 1) {
-            doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+            doReturn(input[1]).when(mImService).monitorInput(anyString(), anyInt());
         }
 
-        final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
-        final TaskChangeNotificationController taskChangeNotificationController = mock(
-                TaskChangeNotificationController.class);
-        doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController();
-        final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
-        doReturn(wmLock).when(atms).getGlobalLock();
+        // StatusBarManagerInternal
+        final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
+        doReturn(sbmi).when(() -> LocalServices.getService(eq(StatusBarManagerInternal.class)));
+    }
 
-        mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService);
-        mWindowManagerService = WindowManagerService.main(
-                context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new);
+    private void setUpActivityTaskManagerService() {
+        // ActivityManagerService
+        mAmService = new ActivityManagerService(
+                new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
+        spyOn(mAmService);
+        doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
+        doNothing().when(mAmService).grantEphemeralAccessLocked(
+                anyInt(), any(), anyInt(), anyInt());
 
-        mWindowManagerService.onInitReady();
+        // ActivityManagerInternal
+        final ActivityManagerInternal amInternal = mAmService.mInternal;
+        spyOn(amInternal);
+        doNothing().when(amInternal).trimApplications();
+        doNothing().when(amInternal).updateCpuStats();
+        doNothing().when(amInternal).updateOomAdj();
+        doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
+        doNothing().when(amInternal).updateActivityUsageStats(
+                any(), anyInt(), anyInt(), any(), any());
+        doNothing().when(amInternal).startProcess(
+                any(), any(), anyBoolean(), anyBoolean(), any(), any());
+        doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
+        LocalServices.addService(ActivityManagerInternal.class, amInternal);
 
-        final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
-        // Display creation is driven by the ActivityManagerService via
-        // ActivityStackSupervisor. We emulate those steps here.
-        DisplayContent displayContent = mWindowManagerService.mRoot
-                .createDisplayContent(display, mock(ActivityDisplay.class));
-        displayContent.reconfigureDisplayLocked();
+        mAtmService = new TestActivityTaskManagerService(mContext, mAmService);
+        LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal());
+    }
 
-        mMockTracker.stopTracking();
+    private void setUpWindowManagerService() {
+        mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
+        mWMPolicy = new TestWindowManagerPolicy(this::getWindowManagerService,
+                mPowerManagerWrapper);
+        mWmService = WindowManagerService.main(
+                mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new);
+        spyOn(mWmService);
+
+        // Setup factory classes to prevent calls to native code.
+        mTransaction = spy(StubTransaction.class);
+        // Return a spied Transaction class than can be used to verify calls.
+        mWmService.mTransactionFactory = () -> mTransaction;
+        // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
+        mWmService.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
+        // Return mocked Surface instances.
+        mWmService.mSurfaceFactory = () -> mock(Surface.class);
+        mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
+                null, null, mTransaction, mWmService.mPowerManagerInternal);
+
+        mWmService.onInitReady();
+        mAmService.setWindowManager(mWmService);
+        mWmService.mDisplayEnabled = true;
+        mWmService.mDisplayReady = true;
+        // Set configuration for default display
+        mWmService.getDefaultDisplayContentLocked().reconfigureDisplayLocked();
+
+        // Mock root, some default display, and home stack.
+        spyOn(mWmService.mRoot);
+        final ActivityDisplay display = mAtmService.mRootActivityContainer.getDefaultDisplay();
+        spyOn(display);
+        spyOn(display.mDisplayContent);
+        final ActivityStack homeStack = display.getStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+        spyOn(homeStack);
+        spyOn(homeStack.mTaskStack);
     }
 
     private void tearDown() {
         waitUntilWindowManagerHandlersIdle();
-        removeLocalServices();
-        mWindowManagerService = null;
-        mWindowManagerPolicy = null;
+        // Unregister display listener from root to avoid issues with subsequent tests.
+        mContext.getSystemService(DisplayManager.class)
+                .unregisterDisplayListener(mAtmService.mRootActivityContainer);
+        // ProptertiesChangesListener is registered in the constructor of WindowManagerService to
+        // a static object, so we need to clean it up in tearDown(), even though we didn't set up
+        // in tests.
+        DeviceConfig.removeOnPropertiesChangedListener(mWmService.mPropertiesChangedListener);
+        mWmService = null;
+        mWMPolicy = null;
+        mPowerManagerWrapper = null;
+
+        tearDownLocalServices();
+        tearDownSystemCore();
+
+        // Needs to explicitly dispose current static threads because there could be messages
+        // scheduled at a later time, and all mocks are invalid when it's executed.
+        DisplayThread.dispose();
+        AnimationThread.dispose();
+        // Reset priority booster because animation thread has been changed.
+        WindowManagerService.sThreadPriorityBooster = new WindowManagerThreadPriorityBooster();
+
+        Mockito.framework().clearInlineMocks();
+    }
+
+    private void tearDownSystemCore() {
         if (mMockitoSession != null) {
             mMockitoSession.finishMocking();
             mMockitoSession = null;
         }
 
-        if (mMockTracker != null) {
-            mMockTracker.close();
-            mMockTracker = null;
+        if (mHandlerThread != null) {
+            // Make sure there are no running messages and then quit the thread so the next test
+            // won't be affected.
+            mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
+                    0 /* timeout */);
         }
     }
 
-    private static void removeLocalServices() {
+    private static void tearDownLocalServices() {
+        LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+        LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
         LocalServices.removeServiceForTest(WindowManagerInternal.class);
         LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+        LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
+        LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
     }
 
     WindowManagerService getWindowManagerService() {
-        return mWindowManagerService;
+        return mWmService;
+    }
+
+    ActivityTaskManagerService getActivityTaskManagerService() {
+        return mAtmService;
+    }
+
+    WindowState.PowerManagerWrapper getPowerManagerWrapper() {
+        return mPowerManagerWrapper;
     }
 
     void cleanupWindowManagerHandlers() {
@@ -229,6 +373,7 @@
         waitHandlerIdle(wm.mH);
         waitHandlerIdle(wm.mAnimationHandler);
         waitHandlerIdle(SurfaceAnimationThread.getHandler());
+        waitHandlerIdle(mHandlerThread.getThreadHandler());
     }
 
     private void waitHandlerIdle(Handler handler) {
@@ -251,4 +396,121 @@
             }
         }
     }
+
+    protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
+        // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
+        // We keep the reference in order to prevent creating it twice.
+        ActivityStackSupervisor mTestStackSupervisor;
+
+        TestActivityTaskManagerService(Context context, ActivityManagerService ams) {
+            super(context);
+            spyOn(this);
+
+            mSupportsMultiWindow = true;
+            mSupportsMultiDisplay = true;
+            mSupportsSplitScreenMultiWindow = true;
+            mSupportsFreeformWindowManagement = true;
+            mSupportsPictureInPicture = true;
+
+            doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+            // allow background activity starts by default
+            doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+            doNothing().when(this).updateCpuStats();
+
+            // AppOpsService
+            final AppOpsService aos = mock(AppOpsService.class);
+            doReturn(aos).when(this).getAppOpsService();
+            // Make sure permission checks aren't overridden.
+            doReturn(AppOpsManager.MODE_DEFAULT)
+                    .when(aos).noteOperation(anyInt(), anyInt(), anyString());
+
+            setUsageStatsManager(LocalServices.getService(UsageStatsManagerInternal.class));
+            ams.mActivityTaskManager = this;
+            ams.mAtmInternal = mInternal;
+            onActivityManagerInternalAdded();
+            initialize(
+                    ams.mIntentFirewall, ams.mPendingIntentController, mHandlerThread.getLooper());
+            spyOn(getLifecycleManager());
+            spyOn(getLockTaskController());
+            spyOn(getTaskChangeNotificationController());
+            initRootActivityContainerMocks();
+        }
+
+        void initRootActivityContainerMocks() {
+            spyOn(mRootActivityContainer);
+            // Invoked during {@link ActivityStack} creation.
+            doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+            // Always keep things awake.
+            doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+            // Called when moving activity to pinned stack.
+            doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+                    anyBoolean());
+        }
+
+        @Override
+        int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+            return userId;
+        }
+
+        @Override
+        protected ActivityStackSupervisor createStackSupervisor() {
+            if (mTestStackSupervisor == null) {
+                mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
+            }
+            return mTestStackSupervisor;
+        }
+    }
+
+    /**
+     * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
+     * setup not available in the test environment. Also specifies an injector for
+     */
+    protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
+
+        TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+            super(service, looper);
+            spyOn(this);
+
+            // Do not schedule idle that may touch methods outside the scope of the test.
+            doNothing().when(this).scheduleIdleLocked();
+            doNothing().when(this).scheduleIdleTimeoutLocked(any());
+            // unit test version does not handle launch wake lock
+            doNothing().when(this).acquireLaunchWakelock();
+            doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
+
+            mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
+
+            initialize();
+        }
+    }
+
+    // TODO: Can we just mock this?
+    private static class AMTestInjector extends ActivityManagerService.Injector {
+        private ServiceThread mHandlerThread;
+
+        AMTestInjector(Context context, ServiceThread handlerThread) {
+            super(context);
+            mHandlerThread = handlerThread;
+        }
+
+        @Override
+        public Context getContext() {
+            return getInstrumentation().getTargetContext();
+        }
+
+        @Override
+        public AppOpsService getAppOpsService(File file, Handler handler) {
+            return null;
+        }
+
+        @Override
+        public Handler getUiHandler(ActivityManagerService service) {
+            return mHandlerThread.getThreadHandler();
+        }
+
+        @Override
+        public boolean isNetworkRestrictedForUid(int uid) {
+            return false;
+        }
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 2cebebf..bcff704 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1277,17 +1277,18 @@
     }
 
     private ActivityRecord createSourceActivity(TestActivityDisplay display) {
-        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+        final ActivityStack stack = display.createStack(display.getWindowingMode(),
                 ACTIVITY_TYPE_STANDARD, true);
         return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
     }
 
     private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) {
-        final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+        final ActivityStack stack = display.createStack(display.getWindowingMode(),
                 ACTIVITY_TYPE_STANDARD, true);
         stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
-        task.setBounds(bounds);
+        // Just work around the unnecessary adjustments for bounds.
+        task.getWindowConfiguration().setBounds(bounds);
     }
 
     private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a0302f6..c83401b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -28,14 +28,19 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertEquals;
@@ -64,6 +69,7 @@
 import android.util.Xml;
 import android.view.DisplayInfo;
 
+import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.app.IVoiceInteractor;
@@ -102,6 +108,7 @@
     public void setUp() throws Exception {
         TaskRecord.setTaskRecordFactory(null);
         mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
+        removeGlobalMinSizeRestriction();
     }
 
     @Test
@@ -165,6 +172,7 @@
 
     /** Ensures that bounds on freeform stacks are not clipped. */
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testAppBounds_FreeFormBounds() {
         final Rect freeFormBounds = new Rect(mParentBounds);
         freeFormBounds.offset(10, 10);
@@ -174,6 +182,7 @@
 
     /** Ensures that fully contained bounds are not clipped. */
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testAppBounds_ContainedBounds() {
         final Rect insetBounds = new Rect(mParentBounds);
         insetBounds.inset(5, 5, 5, 5);
@@ -182,6 +191,7 @@
     }
 
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testFitWithinBounds() {
         final Rect parentBounds = new Rect(10, 10, 200, 200);
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
@@ -221,6 +231,7 @@
 
     /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testBoundsOnModeChangeFreeformToFullscreen() {
         ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
         ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
@@ -248,18 +259,6 @@
     }
 
     /**
-     * This is a temporary hack to trigger an onConfigurationChange at the task level after an
-     * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
-     * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
-     * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
-     */
-    private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
-        activity.setRequestedOrientation(orientation);
-        ConfigurationContainer taskRecord = activity.getParent();
-        taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
-    }
-
-    /**
      * Tests that a task with forced orientation has orientation-consistent bounds within the
      * parent.
      */
@@ -268,49 +267,48 @@
         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
         final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
         DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
         info.logicalWidth = fullScreenBounds.width();
         info.logicalHeight = fullScreenBounds.height();
         ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
         assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
-        // Override display orientation. Normally this is available via DisplayContent, but DC
-        // is mocked-out.
-        display.getRequestedOverrideConfiguration().orientation =
-                Configuration.ORIENTATION_LANDSCAPE;
-        display.onRequestedOverrideConfigurationChanged(
-                display.getRequestedOverrideConfiguration());
+        // Fix the display orientation to landscape which is the natural rotation (0) for the test
+        // display.
+        final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+        dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
         ActivityStack stack = new StackBuilder(mRootActivityContainer)
                 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
         TaskRecord task = stack.getChildAt(0);
         ActivityRecord root = task.getTopActivity();
-        assertEquals(root, task.getTopActivity());
 
         assertEquals(fullScreenBounds, task.getBounds());
 
         // Setting app to fixed portrait fits within parent
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(root, task.getRootActivity());
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
-        assertTrue(task.getBounds().width() < task.getBounds().height());
+        assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
         assertEquals(fullScreenBounds.height(), task.getBounds().height());
 
         // Top activity gets used
         ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
         assertEquals(top, task.getTopActivity());
-        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
-        assertTrue(task.getBounds().width() > task.getBounds().height());
+        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
         assertEquals(task.getBounds().width(), fullScreenBounds.width());
 
         // Setting app to unspecified restores
-        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
+        top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
         assertEquals(fullScreenBounds, task.getBounds());
 
         // Setting app to fixed landscape and changing display
-        setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
-        // simulate display orientation changing (normally done via DisplayContent)
-        display.getRequestedOverrideConfiguration().orientation =
-                Configuration.ORIENTATION_PORTRAIT;
-        display.setBounds(fullScreenBoundsPort);
-        assertTrue(task.getBounds().width() > task.getBounds().height());
+        top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+        // Fix the display orientation to portrait which is 90 degrees for the test display.
+        dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
         assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
 
         // in FREEFORM, no constraint
@@ -323,7 +321,7 @@
 
         // FULLSCREEN letterboxes bounds
         stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-        assertTrue(task.getBounds().width() > task.getBounds().height());
+        assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
         assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
 
         // FREEFORM restores bounds as before
@@ -335,6 +333,7 @@
     public void testIgnoresForcedOrientationWhenParentHandles() {
         final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
         DisplayInfo info = new DisplayInfo();
+        mService.mContext.getDisplay().getDisplayInfo(info);
         info.logicalWidth = fullScreenBounds.width();
         info.logicalHeight = fullScreenBounds.height();
         ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
@@ -355,7 +354,7 @@
 
         // Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the
         // bounds because its parent says it will handle it at a later time.
-        setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+        root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
         assertEquals(root, task.getRootActivity());
         assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
         assertEquals(fullScreenBounds, task.getBounds());
@@ -646,7 +645,7 @@
         final ActivityRecord activity0 = task0.getChildAt(0);
 
         final TaskRecord task1 = getTestTask();
-        final ActivityRecord activity1 = task0.getChildAt(0);
+        final ActivityRecord activity1 = task1.getChildAt(0);
 
         assertEquals(task0.taskId,
                 ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
new file mode 100644
index 0000000..c143969
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+class TestActivityDisplay extends ActivityDisplay {
+    private final ActivityStackSupervisor mSupervisor;
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor) {
+        return create(supervisor, SystemServicesTestRule.sNextDisplayId++);
+    }
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor, DisplayInfo info) {
+        return create(supervisor, SystemServicesTestRule.sNextDisplayId++, info);
+    }
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+        final DisplayInfo info = new DisplayInfo();
+        supervisor.mService.mContext.getDisplay().getDisplayInfo(info);
+        return create(supervisor, displayId, info);
+    }
+
+    static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
+            DisplayInfo info) {
+        if (displayId == DEFAULT_DISPLAY) {
+            synchronized (supervisor.mService.mGlobalLock) {
+                return new TestActivityDisplay(supervisor,
+                        supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
+            }
+        }
+        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+                info, DEFAULT_DISPLAY_ADJUSTMENTS);
+
+        synchronized (supervisor.mService.mGlobalLock) {
+            return new TestActivityDisplay(supervisor, display);
+        }
+    }
+
+    private TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+        super(supervisor.mService.mRootActivityContainer, display);
+        // Normally this comes from display-properties as exposed by WM. Without that, just
+        // hard-code to FULLSCREEN for tests.
+        setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mSupervisor = supervisor;
+        spyOn(this);
+        spyOn(mDisplayContent);
+        doAnswer(invocation -> {
+            // Bypass all the rotation animation and display freezing stuff for testing and just
+            // set the rotation we want for the display
+            final DisplayContent dc = mDisplayContent;
+            final int oldRotation = dc.getRotation();
+            final int rotation = dc.getDisplayRotation().rotationForOrientation(
+                    dc.getLastOrientation(), oldRotation);
+            if (oldRotation == rotation) {
+                return false;
+            }
+            dc.setLayoutNeeded();
+            dc.setRotation(rotation);
+            return true;
+        }).when(mDisplayContent).updateRotationUnchecked(anyBoolean());
+    }
+
+    @SuppressWarnings("TypeParameterUnusedInFormals")
+    @Override
+    ActivityStack createStackUnchecked(int windowingMode, int activityType,
+            int stackId, boolean onTop) {
+        return new ActivityTestsBase.StackBuilder(mSupervisor.mRootActivityContainer)
+                .setDisplay(this)
+                .setWindowingMode(windowingMode)
+                .setActivityType(activityType)
+                .setStackId(stackId)
+                .setOnTop(onTop)
+                .setCreateActivity(false)
+                .build();
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 2e5ce69..bb89446 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -40,12 +40,14 @@
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowState.PowerManagerWrapper;
 
 import java.io.PrintWriter;
 import java.util.function.Supplier;
 
 class TestWindowManagerPolicy implements WindowManagerPolicy {
     private final Supplier<WindowManagerService> mWmSupplier;
+    private final PowerManagerWrapper mPowerManagerWrapper;
 
     int mRotationToReport = 0;
     boolean mKeyguardShowingAndNotOccluded = false;
@@ -53,8 +55,10 @@
 
     private Runnable mRunnableWhenAddingSplashScreen;
 
-    TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
+    TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier,
+            PowerManagerWrapper powerManagerWrapper) {
         mWmSupplier = wmSupplier;
+        mPowerManagerWrapper = powerManagerWrapper;
     }
 
     @Override
@@ -121,7 +125,7 @@
             doReturn(mock(IBinder.class)).when(iWindow).asBinder();
             window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
                     "Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
-                    mock(Session.class), iWindow);
+                    mock(Session.class), iWindow, mPowerManagerWrapper);
             atoken.startingWindow = window;
         }
         if (mRunnableWhenAddingSplashScreen != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index acfc2ea..921f105 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -113,6 +114,7 @@
 
     @Test
     public void testAddChildSetsSurfacePosition() {
+        reset(mTransaction);
         try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
             WindowContainer child = new WindowContainer(mWm);
             child.setBounds(1, 1, 10, 10);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 06bcdf8..60cefe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -57,6 +57,7 @@
 
     private final IWindow mIWindow = new TestIWindow();
     private final Rect mEmptyRect = new Rect();
+    private DisplayContent mTestDisplayContent;
 
     static class FrameTestWindowState extends WindowState {
         boolean mDockedResizingForTest = false;
@@ -77,6 +78,9 @@
     @Before
     public void setUp() throws Exception {
         mStubStack = mock(TaskStack.class);
+        DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
+        testDisplayInfo.displayCutout = null;
+        mTestDisplayContent = createNewDisplay(testDisplayInfo);
     }
 
     // Do not use this function directly in the tests below. Instead, use more explicit function
@@ -100,6 +104,10 @@
         assertRect(w.getStableInsets(), left, top, right, bottom);
     }
 
+    private void assertFrame(WindowState w, Rect frame) {
+        assertEquals(w.getFrameLw(), frame);
+    }
+
     private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
         assertRect(w.getFrameLw(), left, top, right, bottom);
     }
@@ -380,9 +388,10 @@
     }
 
     @Test
+    @FlakyTest(bugId = 137879065)
     public void testLayoutLetterboxedWindow() {
         // First verify task behavior in multi-window mode.
-        final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
+        final DisplayInfo displayInfo = mTestDisplayContent.getDisplayInfo();
         final int logicalWidth = displayInfo.logicalWidth;
         final int logicalHeight = displayInfo.logicalHeight;
 
@@ -413,13 +422,14 @@
         final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
         Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
         config.windowConfiguration.setBounds(cf);
+        config.windowConfiguration.setAppBounds(cf);
         w.mAppToken.onRequestedOverrideConfigurationChanged(config);
         pf.set(0, 0, logicalWidth, logicalHeight);
         task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         task.setBounds(null);
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw();
-        assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+        assertFrame(w, cf);
         assertContentFrame(w, cf);
         assertContentInset(w, 0, 0, 0, 0);
     }
@@ -483,7 +493,7 @@
         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
         task.setWindowingMode(WINDOWING_MODE_FREEFORM);
 
-        DisplayContent dc = mWm.getDefaultDisplayContentLocked();
+        DisplayContent dc = mTestDisplayContent;
         dc.mInputMethodTarget = w;
         WindowState mockIme = mock(WindowState.class);
         Mockito.doReturn(true).when(mockIme).isVisibleNow();
@@ -537,7 +547,7 @@
         attrs.width = width;
         attrs.height = height;
 
-        AppWindowToken token = createAppWindowToken(mWm.getDefaultDisplayContentLocked(),
+        AppWindowToken token = createAppWindowToken(mTestDisplayContent,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
 
         FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, token, attrs);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 36698ea..b731628 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -346,24 +346,28 @@
         firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
-        reset(sPowerManagerWrapper);
+        final WindowState.PowerManagerWrapper powerManagerWrapper =
+                mSystemServicesTestRule.getPowerManagerWrapper();
+        reset(powerManagerWrapper);
         firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+        verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
 
-        reset(sPowerManagerWrapper);
+        reset(powerManagerWrapper);
         secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
-        verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+        verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
     }
 
     private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
             boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) {
-        reset(sPowerManagerWrapper);
+        final WindowState.PowerManagerWrapper powerManagerWrapper =
+                mSystemServicesTestRule.getPowerManagerWrapper();
+        reset(powerManagerWrapper);
         appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
 
         if (expectedWakeupCalled) {
-            verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+            verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
         } else {
-            verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
+            verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
         }
         // If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
         // because the state will be consumed.
@@ -517,13 +521,19 @@
 
     @Test
     public void testGetTransformationMatrix() {
+        final int PARENT_WINDOW_OFFSET = 1;
+        final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
+        final int WINDOW_OFFSET = 3;
+        final float OFFSET_SUM =
+                PARENT_WINDOW_OFFSET + DISPLAY_IN_PARENT_WINDOW_OFFSET + WINDOW_OFFSET;
+
         synchronized (mWm.mGlobalLock) {
             final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
-            win0.getFrameLw().offsetTo(1, 0);
 
             final DisplayContent dc = createNewDisplay();
+            win0.getFrameLw().offsetTo(PARENT_WINDOW_OFFSET, 0);
             dc.reparentDisplayContent(win0, win0.getSurfaceControl());
-            dc.updateLocation(win0, 2, 0);
+            dc.updateLocation(win0, DISPLAY_IN_PARENT_WINDOW_OFFSET, 0);
 
             final float[] values = new float[9];
             final Matrix matrix = new Matrix();
@@ -531,12 +541,12 @@
             final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
             win1.mHasSurface = true;
             win1.mSurfaceControl = mock(SurfaceControl.class);
-            win1.getFrameLw().offsetTo(3, 0);
+            win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0);
             win1.updateSurfacePosition(t);
             win1.getTransformationMatrix(values, matrix);
 
             matrix.getValues(values);
-            assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+            assertEquals(OFFSET_SUM, values[Matrix.MTRANS_X], 0f);
             assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index dc461d1..d1cf1c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -20,8 +20,6 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.os.Process.SYSTEM_UID;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
 import static android.view.View.VISIBLE;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -38,33 +36,27 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
 
 import static org.mockito.Mockito.mock;
 
-
 import android.content.Context;
 import android.content.res.Configuration;
-import android.hardware.display.DisplayManagerGlobal;
 import android.testing.DexmakerShareClassLoaderRule;
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.IWindow;
-import android.view.Surface;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowManager;
 
 import com.android.server.AttributeCache;
-import com.android.server.wm.utils.MockTracker;
 
 import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Rule;
 
-import java.io.IOException;
 import java.util.HashSet;
 import java.util.LinkedList;
 
@@ -79,10 +71,6 @@
     WindowManagerService mWm;
     private final IWindow mIWindow = new TestIWindow();
     private Session mMockSession;
-    // The default display is removed in {@link #setUp} and then we iterate over all displays to
-    // make sure we don't collide with any existing display. If we run into no other display, the
-    // added display should be treated as default. This cannot be the default display
-    private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
     static int sNextStackId = 1000;
 
     /** Non-default display. */
@@ -99,8 +87,6 @@
     WindowState mChildAppWindowBelow;
     HashSet<WindowState> mCommonWindows;
 
-    private MockTracker mMockTracker;
-
     /**
      * Spied {@link Transaction} class than can be used to verify calls.
      */
@@ -112,49 +98,27 @@
     @Rule
     public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
 
-    static WindowState.PowerManagerWrapper sPowerManagerWrapper;
-
     @BeforeClass
     public static void setUpOnceBase() {
         AttributeCache.init(getInstrumentation().getTargetContext());
-
-        sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
-    }
-
-    @AfterClass
-    public static void tearDownOnceBase() throws IOException {
-        sPowerManagerWrapper = null;
     }
 
     @Before
     public void setUpBase() {
-        mMockTracker = new MockTracker();
-
         // If @Before throws an exception, the error isn't logged. This will make sure any failures
         // in the set up are clear. This can be removed when b/37850063 is fixed.
         try {
             mMockSession = mock(Session.class);
-            mTransaction = spy(StubTransaction.class);
 
             final Context context = getInstrumentation().getTargetContext();
 
             mWm = mSystemServicesTestRule.getWindowManagerService();
-
-            // Setup factory classes to prevent calls to native code.
-
-            // Return a spied Transaction class than can be used to verify calls.
-            mWm.mTransactionFactory = () -> mTransaction;
-            // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
-            mWm.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
-            // Return mocked Surface instances.
-            mWm.mSurfaceFactory = () -> mock(Surface.class);
+            mTransaction = mSystemServicesTestRule.mTransaction;
 
             beforeCreateDisplay();
 
             context.getDisplay().getDisplayInfo(mDisplayInfo);
             mDisplayContent = createNewDisplay();
-            mWm.mDisplayEnabled = true;
-            mWm.mDisplayReady = true;
 
             // Set-up some common windows.
             mCommonWindows = new HashSet<>();
@@ -211,12 +175,6 @@
                     nonCommonWindows.pollLast().removeImmediately();
                 }
 
-                for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
-                    final DisplayContent displayContent = mWm.mRoot.mChildren.get(i);
-                    if (!displayContent.isDefaultDisplay) {
-                        displayContent.removeImmediately();
-                    }
-                }
                 // Remove app transition & window freeze timeout callbacks to prevent unnecessary
                 // actions after test.
                 mWm.getDefaultDisplayContentLocked().mAppTransition
@@ -230,11 +188,7 @@
         } catch (Exception e) {
             Log.e(TAG, "Failed to tear down test", e);
             throw e;
-        } finally {
-            mMockTracker.close();
-            mMockTracker = null;
         }
-
     }
 
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
@@ -356,12 +310,13 @@
     WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
             int ownerId, boolean ownerCanAddInternalSystemWindow) {
         return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
-                mWm, mMockSession, mIWindow);
+                mWm, mMockSession, mIWindow, mSystemServicesTestRule.getPowerManagerWrapper());
     }
 
     static WindowState createWindow(WindowState parent, int type, WindowToken token,
             String name, int ownerId, boolean ownerCanAddInternalSystemWindow,
-            WindowManagerService service, Session session, IWindow iWindow) {
+            WindowManagerService service, Session session, IWindow iWindow,
+            WindowState.PowerManagerWrapper powerManagerWrapper) {
         synchronized (service.mGlobalLock) {
             final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
             attrs.setTitle(name);
@@ -369,7 +324,7 @@
             final WindowState w = new WindowState(service, session, iWindow, token, parent,
                     OP_NONE,
                     0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow,
-                    sPowerManagerWrapper);
+                    powerManagerWrapper);
             // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
             // adding it to the token...
             token.addWindow(w);
@@ -408,13 +363,11 @@
     }
 
     /** Creates a {@link DisplayContent} and adds it to the system. */
-    DisplayContent createNewDisplay(DisplayInfo displayInfo) {
-        final int displayId = sNextDisplayId++;
-        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        synchronized (mWm.mGlobalLock) {
-            return new DisplayContent(display, mWm, mock(ActivityDisplay.class));
-        }
+    DisplayContent createNewDisplay(DisplayInfo info) {
+        final ActivityDisplay display =
+                TestActivityDisplay.create(mWm.mAtmService.mStackSupervisor, info);
+        mWm.mAtmService.mRootActivityContainer.addChild(display, POSITION_TOP);
+        return display.mDisplayContent;
     }
 
     /**
@@ -428,17 +381,7 @@
         DisplayInfo displayInfo = new DisplayInfo();
         displayInfo.copyFrom(mDisplayInfo);
         displayInfo.state = displayState;
-        final int displayId = sNextDisplayId++;
-        final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
-                displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
-        synchronized (mWm.mGlobalLock) {
-            // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
-            // We skip those steps here.
-            final ActivityDisplay mockAd = mock(ActivityDisplay.class);
-            final DisplayContent displayContent = mWm.mRoot.createDisplayContent(display, mockAd);
-            displayContent.reconfigureDisplayLocked();
-            return displayContent;
-        }
+        return createNewDisplay(displayInfo);
     }
 
     /** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
index a6e675a..7f09482 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
@@ -89,7 +89,7 @@
 
         for (final Object mock : mMocks.keySet()) {
             if (MockUtil.isMock(mock)) {
-                Mockito.reset(mock);
+                mMockitoFramework.clearInlineMock(mock);
             }
         }
         mMocks.clear();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index b2fde54..b2ac549 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -20,6 +20,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
@@ -36,6 +37,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.pm.ShortcutServiceInternal;
+import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.hardware.soundtrigger.IRecognitionStatusCallback;
@@ -80,6 +82,7 @@
 import com.android.server.UiThread;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.soundtrigger.SoundTriggerInternal;
+import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.io.FileDescriptor;
@@ -92,7 +95,7 @@
  */
 public class VoiceInteractionManagerService extends SystemService {
     static final String TAG = "VoiceInteractionManagerService";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = true; // TODO(b/133242016) STOPSHIP: change to false before R ships
 
     final Context mContext;
     final ContentResolver mResolver;
@@ -154,19 +157,37 @@
     }
 
     @Override
-    public void onStartUser(int userHandle) {
-        mServiceStub.initForUser(userHandle);
+    public void onStartUser(@NonNull UserInfo userInfo) {
+        if (DEBUG) Slog.d(TAG, "onStartUser(" + userInfo + ")");
+
+        if (!userInfo.isFull()) {
+            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
+            return;
+        }
+        mServiceStub.initForUser(userInfo.id);
     }
 
     @Override
-    public void onUnlockUser(int userHandle) {
-        mServiceStub.initForUser(userHandle);
+    public void onUnlockUser(@NonNull UserInfo userInfo) {
+        if (DEBUG) Slog.d(TAG, "onUnlockUser(" + userInfo + ")");
+
+        if (!userInfo.isFull()) {
+            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
+            return;
+        }
+        mServiceStub.initForUser(userInfo.id);
         mServiceStub.switchImplementationIfNeeded(false);
     }
 
     @Override
-    public void onSwitchUser(int userHandle) {
-        mServiceStub.switchUser(userHandle);
+    public void onSwitchUser(@NonNull UserInfo userInfo) {
+        if (DEBUG) Slog.d(TAG, "onSwitchUser(" + userInfo + ")");
+
+        if (!userInfo.isFull()) {
+            if (DEBUG) Slog.d(TAG,  "***** skipping on non-full user " + userInfo);
+            return;
+        }
+        mServiceStub.switchUser(userInfo.id);
     }
 
     class LocalService extends VoiceInteractionManagerInternal {
@@ -270,6 +291,20 @@
         }
 
         public void initForUser(int userHandle) {
+            final TimingsTraceAndSlog t;
+            if (DEBUG) {
+                t = TimingsTraceAndSlog.newAsyncLog();
+                t.traceBegin("VoiceInteractionSvc.initForUser(" + userHandle + ")");
+            } else {
+                t = null;
+            }
+            initForUserNoTracing(userHandle);
+            if (t != null) {
+                t.traceEnd();
+            }
+        }
+
+        private void initForUserNoTracing(@UserIdInt int userHandle) {
             if (DEBUG) Slog.d(TAG, "**************** initForUser user=" + userHandle);
             String curInteractorStr = Settings.Secure.getStringForUser(
                     mContext.getContentResolver(),
@@ -426,6 +461,20 @@
         }
 
         void switchImplementationIfNeededLocked(boolean force) {
+            final TimingsTraceAndSlog t;
+            if (DEBUG) {
+                t = TimingsTraceAndSlog.newAsyncLog();
+                t.traceBegin("VoiceInteractionSvc.switchImplementation(" + mCurUser + ")");
+            } else {
+                t = null;
+            }
+            switchImplementationIfNeededNoTracingLocked(force);
+            if (t != null) {
+                t.traceEnd();
+            }
+        }
+
+        void switchImplementationIfNeededNoTracingLocked(boolean force) {
             if (!mSafeMode) {
                 String curService = Settings.Secure.getStringForUser(
                         mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp
new file mode 100644
index 0000000..3c916a6
--- /dev/null
+++ b/services/wifi/Android.bp
@@ -0,0 +1,11 @@
+// Interfaces between the core system and the wifi mainline module.
+java_library_static {
+    name: "services.wifi",
+    srcs: [
+        "java/**/*.java",
+        "java/**/*.aidl",
+    ],
+    libs: [
+        "services.net",
+    ],
+}
diff --git a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
new file mode 100644
index 0000000..eadc726
--- /dev/null
+++ b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2019, 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 perNmissions and
+ * limitations under the License.
+ */
+package android.net.wifi;
+
+/** @hide */
+interface IWifiStackConnector {
+     IBinder retrieveApiServiceImpl(String serviceName);
+     boolean startApiService(String serviceName);
+}
diff --git a/services/wifi/java/android/net/wifi/WifiStackClient.java b/services/wifi/java/android/net/wifi/WifiStackClient.java
new file mode 100644
index 0000000..fa66e4c
--- /dev/null
+++ b/services/wifi/java/android/net/wifi/WifiStackClient.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 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.net.wifi;
+
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.ConnectivityModuleConnector;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Service used to communicate with the wifi stack, which could be running in a separate
+ * module.
+ * @hide
+ */
+public class WifiStackClient {
+    public static final String PERMISSION_MAINLINE_WIFI_STACK =
+            "android.permission.MAINLINE_WIFI_STACK";
+
+    private static final String TAG = WifiStackClient.class.getSimpleName();
+    private static WifiStackClient sInstance;
+
+    private WifiStackClient() { }
+
+    /**
+     * Get the WifiStackClient singleton instance.
+     */
+    public static synchronized WifiStackClient getInstance() {
+        if (sInstance == null) {
+            sInstance = new WifiStackClient();
+        }
+        return sInstance;
+    }
+
+    private class WifiStackConnection implements
+            ConnectivityModuleConnector.ModuleServiceCallback {
+        @Override
+        public void onModuleServiceConnected(IBinder service) {
+            Log.i(TAG, "Wifi stack connected");
+
+            registerWifiStackService(service);
+            IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
+            registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
+            registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
+            registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
+            registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
+            registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+        }
+    }
+
+    private void registerWifiStackService(@NonNull IBinder service) {
+        ServiceManager.addService(Context.WIFI_STACK_SERVICE, service,
+                false /* allowIsolated */,
+                DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+        Log.i(TAG, "Wifi stack service registered");
+    }
+
+    private void registerApiServiceAndStart(
+            IWifiStackConnector stackConnector, String serviceName) {
+        IBinder service = null;
+        try {
+            service = stackConnector.retrieveApiServiceImpl(serviceName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failed to retrieve service impl " + serviceName, e);
+        }
+        if (service == null) {
+            Log.i(TAG, "Service " + serviceName + " not available");
+            return;
+        }
+        Log.i(TAG, "Registering " + serviceName);
+        ServiceManager.addService(serviceName, service);
+
+        boolean success = false;
+        try {
+            success = stackConnector.startApiService(serviceName);
+        } catch (RemoteException e) {
+            throw new RuntimeException("Failed to start service " + serviceName, e);
+        }
+        if (!success) {
+            throw new RuntimeException("Service " + serviceName + " start failed");
+        }
+    }
+
+    /**
+     * Start the wifi stack. Should be called only once on device startup.
+     *
+     * <p>This method will start the wifi stack either in the wifi stack
+     * process, or inside the system server on devices that do not support the wifi stack
+     * module.
+     */
+    public void start() {
+        Log.i(TAG, "Starting wifi stack");
+        ConnectivityModuleConnector.getInstance().startModuleService(
+                IWifiStackConnector.class.getName(), PERMISSION_MAINLINE_WIFI_STACK,
+                new WifiStackConnection());
+    }
+}
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index 50c3cd9..95a47e1 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -33,6 +33,8 @@
  */
 public class Session {
 
+    public static final String LOG_TAG = "Session";
+
     public static final String START_SESSION = "START_SESSION";
     public static final String START_EXTERNAL_SESSION = "START_EXTERNAL_SESSION";
     public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
@@ -45,6 +47,9 @@
     public static final String EXTERNAL_INDICATOR = "E-";
     public static final String TRUNCATE_STRING = "...";
 
+    // Prevent infinite recursion by setting a reasonable limit.
+    private static final int SESSION_RECURSION_LIMIT = 25;
+
     /**
      * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
      * if the Session is canceled.
@@ -226,6 +231,15 @@
 
     // Builds full session id recursively
     private String getFullSessionId() {
+        return getFullSessionId(0);
+    }
+
+    // keep track of calls and bail if we hit the recursion limit
+    private String getFullSessionId(int parentCount) {
+        if (parentCount >= SESSION_RECURSION_LIMIT) {
+            Log.w(LOG_TAG, "getFullSessionId: Hit recursion limit!");
+            return TRUNCATE_STRING + mSessionId;
+        }
         // Cache mParentSession locally to prevent a concurrency problem where
         // Log.endParentSessions() is called while a logging statement is running (Log.i, for
         // example) and setting mParentSession to null in a different thread after the null check
@@ -235,42 +249,57 @@
             return mSessionId;
         } else {
             if (Log.VERBOSE) {
-                return parentSession.getFullSessionId() +
+                return parentSession.getFullSessionId(parentCount + 1)
                         // Append "_X" to subsession to show subsession designation.
-                        SESSION_SEPARATION_CHAR_CHILD + mSessionId;
+                        + SESSION_SEPARATION_CHAR_CHILD + mSessionId;
             } else {
                 // Only worry about the base ID at the top of the tree.
-                return parentSession.getFullSessionId();
+                return parentSession.getFullSessionId(parentCount + 1);
             }
 
         }
     }
 
-    // Print out the full Session tree from any subsession node
-    public String printFullSessionTree() {
-        // Get to the top of the tree
+    private Session getRootSession(String callingMethod) {
+        int currParentCount = 0;
         Session topNode = this;
         while (topNode.getParentSession() != null) {
+            if (currParentCount >= SESSION_RECURSION_LIMIT) {
+                Log.w(LOG_TAG, "getRootSession: Hit recursion limit from " + callingMethod);
+                break;
+            }
             topNode = topNode.getParentSession();
+            currParentCount++;
         }
-        return topNode.printSessionTree();
+        return topNode;
+    }
+
+    // Print out the full Session tree from any subsession node
+    public String printFullSessionTree() {
+        return getRootSession("printFullSessionTree").printSessionTree();
     }
 
     // Recursively move down session tree using DFS, but print out each node when it is reached.
-    public String printSessionTree() {
+    private String printSessionTree() {
         StringBuilder sb = new StringBuilder();
-        printSessionTree(0, sb);
+        printSessionTree(0, sb, 0);
         return sb.toString();
     }
 
-    private void printSessionTree(int tabI, StringBuilder sb) {
+    private void printSessionTree(int tabI, StringBuilder sb, int currChildCount) {
+        // Prevent infinite recursion.
+        if (currChildCount >= SESSION_RECURSION_LIMIT) {
+            Log.w(LOG_TAG, "printSessionTree: Hit recursion limit!");
+            sb.append(TRUNCATE_STRING);
+            return;
+        }
         sb.append(toString());
         for (Session child : mChildSessions) {
             sb.append("\n");
             for (int i = 0; i <= tabI; i++) {
                 sb.append("\t");
             }
-            child.printSessionTree(tabI + 1, sb);
+            child.printSessionTree(tabI + 1, sb, currChildCount + 1);
         }
     }
 
@@ -279,11 +308,17 @@
     // recent) will be truncated to "..."
     public String getFullMethodPath(boolean truncatePath) {
         StringBuilder sb = new StringBuilder();
-        getFullMethodPath(sb, truncatePath);
+        getFullMethodPath(sb, truncatePath, 0);
         return sb.toString();
     }
 
-    private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath) {
+    private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath,
+            int parentCount) {
+        if (parentCount >= SESSION_RECURSION_LIMIT) {
+            Log.w(LOG_TAG, "getFullMethodPath: Hit recursion limit!");
+            sb.append(TRUNCATE_STRING);
+            return;
+        }
         // Return cached value for method path. When returning the truncated path, recalculate the
         // full path without using the cached value.
         if (!TextUtils.isEmpty(mFullMethodPathCache) && !truncatePath) {
@@ -296,7 +331,7 @@
             // Check to see if the session has been renamed yet. If it has not, then the session
             // has not been continued.
             isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
-            parentSession.getFullMethodPath(sb, truncatePath);
+            parentSession.getFullMethodPath(sb, truncatePath, parentCount + 1);
             sb.append(SUBSESSION_SEPARATION_CHAR);
         }
         // Encapsulate the external session's method name so it is obvious what part of the session
@@ -319,13 +354,10 @@
             mFullMethodPathCache = sb.toString();
         }
     }
+
     // Recursively move to the top of the tree to see if the parent session is external.
     private boolean isSessionExternal() {
-        if (getParentSession() == null) {
-            return isExternal();
-        } else {
-            return getParentSession().isSessionExternal();
-        }
+        return getRootSession("isSessionExternal").isExternal();
     }
 
     @Override
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index c4a9ee9..42a5501 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -692,7 +692,7 @@
         }
 
         /**
-         * Contains all sent text-based SMS messages in the SMS app.
+         * Contains all draft text-based SMS messages in the SMS app.
          */
         public static final class Draft implements BaseColumns, TextBasedSmsColumns {
 
@@ -808,7 +808,15 @@
         }
 
         /**
-         * Contains all sent text-based SMS messages in the SMS app.
+         * Contains a view of SMS conversations (also referred to as threads). This is similar to
+         * {@link Threads}, but only includes SMS messages and columns relevant to SMS
+         * conversations.
+         * <p>
+         * Note that this view ignores any information about MMS messages, it is a
+         * view of conversations as if MMS messages did not exist at all. This means that all
+         * relevant information, such as snippets and message count, will ignore any MMS messages
+         * that might be in the same thread through other views and present only data based on the
+         * SMS messages in that thread.
          */
         public static final class Conversations
                 implements BaseColumns, TextBasedSmsColumns {
@@ -3230,6 +3238,8 @@
 
         /**
          * The {@code content://} style URL for locked messages in this table.
+         * <P>This {@link Uri} is used to check at most one locked message found in the union of MMS
+         * and SMS messages. Also this will return only _id column in response.</P>
          */
         public static final Uri CONTENT_LOCKED_URI = Uri.parse(
                 "content://mms-sms/locked");
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 66cc6eb..60de214 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1421,6 +1421,13 @@
             "show_4g_for_lte_data_icon_bool";
 
     /**
+     * Boolean indicating if default data account should show 4G icon when in 3G.
+     * @hide
+     */
+    public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL =
+            "show_4g_for_3g_data_icon_bool";
+
+    /**
      * Boolean indicating if lte+ icon should be shown if available
      * @hide
      */
@@ -2675,6 +2682,22 @@
             "5g_icon_configuration_string";
 
     /**
+     * Timeout in second for displaying 5G icon, default value is 0 which means the timer is
+     * disabled.
+     *
+     * System UI will show the 5G icon and start a timer with the timeout from this config when the
+     * device connects to a 5G cell. System UI stops displaying 5G icon when both the device
+     * disconnects from 5G cell and the timer is expired.
+     *
+     * If 5G is reacquired during this timer, the timer is canceled and restarted when 5G is next
+     * lost. Allows us to momentarily lose 5G without blinking the icon.
+     *
+     * @hide
+     */
+    public static final String KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT =
+            "5g_icon_display_grace_period_sec_int";
+
+    /**
      * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
      * this feature.
      * @hide
@@ -3447,6 +3470,7 @@
         sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, false);
+        sDefaults.putBoolean(KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL, false);
         sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, "");
         sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
         sDefaults.putBoolean(KEY_HIDE_LTE_PLUS_DATA_ICON_BOOL, true);
@@ -3486,6 +3510,7 @@
         sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
         sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
                 "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+        sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0);
         sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
         /* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
         sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 258a873..432978d 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -17,6 +17,7 @@
 package android.telephony;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -61,7 +62,7 @@
         mType = type;
 
         // Only allow INT_MAX if unknown string mcc/mnc
-        if (mcc == null || mcc.matches("^[0-9]{3}$")) {
+        if (mcc == null || isMcc(mcc)) {
             mMccStr = mcc;
         } else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
             // If the mccStr is empty or unknown, set it as null.
@@ -73,7 +74,7 @@
             log("invalid MCC format: " + mcc);
         }
 
-        if (mnc == null || mnc.matches("^[0-9]{2,3}$")) {
+        if (mnc == null || isMnc(mnc)) {
             mMncStr = mnc;
         } else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
             // If the mncStr is empty or unknown, set it as null.
@@ -262,4 +263,30 @@
         if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
         return value;
     }
+
+    /** @hide */
+    private static boolean isMcc(@NonNull String mcc) {
+        // ensure no out of bounds indexing
+        if (mcc.length() != 3) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < 3; i++) {
+            if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
+
+    /** @hide */
+    private static boolean isMnc(@NonNull String mnc) {
+        // ensure no out of bounds indexing
+        if (mnc.length() < 2 || mnc.length() > 3) return false;
+
+        // Character.isDigit allows all unicode digits, not just [0-9]
+        for (int i = 0; i < mnc.length(); i++) {
+            if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
+        }
+
+        return true;
+    }
 }
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 5d8d793..89d30c0d 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -42,6 +42,8 @@
 
     private static final String TAG = DataServiceCallback.class.getSimpleName();
 
+    private static final boolean DBG = true;
+
     /**
      * Result of data requests
      * @hide
@@ -81,6 +83,7 @@
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
+                if (DBG) Rlog.d(TAG, "onSetupDataCallComplete");
                 callback.onSetupDataCallComplete(result, response);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
@@ -98,6 +101,7 @@
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
+                if (DBG) Rlog.d(TAG, "onDeactivateDataCallComplete");
                 callback.onDeactivateDataCallComplete(result);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
@@ -169,6 +173,7 @@
         IDataServiceCallback callback = mCallback.get();
         if (callback != null) {
             try {
+                if (DBG) Rlog.d(TAG, "onDataCallListChanged");
                 callback.onDataCallListChanged(dataCallList);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
index 8e1324b..6187e67 100644
--- a/telephony/java/android/telephony/ims/ImsException.java
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -39,11 +39,11 @@
      */
     public static final int CODE_ERROR_UNSPECIFIED = 0;
     /**
-     * The operation has failed because there is no {@link ImsService} available to service it. This
-     * may be due to an {@link ImsService} crash or other illegal state.
+     * The operation has failed because there is no remote process available to service it. This
+     * may be due to a process crash or other illegal state.
      * <p>
      * This is a temporary error and the operation may be retried until the connection to the
-     * {@link ImsService} is restored.
+     * remote process is restored.
      */
     public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
 
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
index 691cfba..4b98b79 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsFeature.aidl
@@ -16,10 +16,38 @@
 
 package android.telephony.ims.aidl;
 
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.feature.CapabilityChangeRequest;
+
+import java.util.List;
+
 /**
  * See RcsFeature for more information.
  * {@hide}
  */
 interface IImsRcsFeature {
-    //Empty Default Implementation
+    // Not oneway because we need to verify this completes before doing anything else.
+    void setListener(IRcsFeatureListener listener);
+    int queryCapabilityStatus();
+    // Inherited from ImsFeature
+    int getFeatureState();
+    oneway void addCapabilityCallback(IImsCapabilityCallback c);
+    oneway void removeCapabilityCallback(IImsCapabilityCallback c);
+    oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest r,
+            IImsCapabilityCallback c);
+    oneway void queryCapabilityConfiguration(int capability, int radioTech,
+            IImsCapabilityCallback c);
+    // RcsPresenceExchangeImplBase specific api
+    oneway void requestCapabilities(in List<Uri> uris, int operationToken);
+    oneway void updateCapabilities(in RcsContactUceCapability capabilities, int operationToken);
+    // RcsSipOptionsImplBase specific api
+    oneway void sendCapabilityRequest(in Uri contactUri,
+            in RcsContactUceCapability capabilities, int operationToken);
+    oneway void respondToCapabilityRequest(in String contactUri,
+            in RcsContactUceCapability ownCapabilities, int operationToken);
+    oneway void respondToCapabilityRequestWithError(in Uri contactUri, int code, in String reason,
+            int operationToken);
 }
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl
new file mode 100644
index 0000000..881b477
--- /dev/null
+++ b/telephony/java/android/telephony/ims/aidl/IRcsFeatureListener.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.aidl;
+
+import android.net.Uri;
+import android.telephony.ims.RcsContactUceCapability;
+
+import java.util.List;
+
+/**
+ * Listener interface for updates from the RcsFeature back to the framework.
+ * {@hide}
+ */
+interface IRcsFeatureListener {
+    //RcsCapabilityExchange specific
+    oneway void onCommandUpdate(int commandCode, int operationToken);
+    // RcsPresenceExchangeImplBase Specific
+    oneway void onNetworkResponse(int code, in String reason, int operationToken);
+    oneway void onCapabilityRequestResponsePresence(in List<RcsContactUceCapability> infos,
+    int operationToken);
+    oneway void onNotifyUpdateCapabilities();
+    oneway void onUnpublish();
+    // RcsSipOptionsImplBase specific
+    oneway void onCapabilityRequestResponseOptions(int code, in String reason,
+            in RcsContactUceCapability info, int operationToken);
+    oneway void onRemoteCapabilityRequest(in Uri contactUri, in RcsContactUceCapability remoteInfo,
+            int operationToken);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 8f89899..3a9979d 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -33,12 +33,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
-import java.util.WeakHashMap;
 
 /**
  * Base class for all IMS features that are supported by the framework. Use a concrete subclass
@@ -52,35 +48,6 @@
     private static final String LOG_TAG = "ImsFeature";
 
     /**
-     * Action to broadcast when ImsService is up.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_UP =
-            "com.android.ims.IMS_SERVICE_UP";
-
-    /**
-     * Action to broadcast when ImsService is down.
-     * Internal use only.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String ACTION_IMS_SERVICE_DOWN =
-            "com.android.ims.IMS_SERVICE_DOWN";
-
-    /**
-     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
-     * A long value; the phone ID corresponding to the IMS service coming up or down.
-     * Only defined here separately for compatibility purposes with the old ImsService.
-     *
-     * @hide
-     */
-    public static final String EXTRA_PHONE_ID = "android:phone_id";
-
-    /**
      * Invalid feature value
      * @hide
      */
@@ -335,8 +302,8 @@
     /** @hide */
     protected final Object mLock = new Object();
 
-    private final Set<IImsFeatureStatusCallback> mStatusCallbacks =
-            Collections.newSetFromMap(new WeakHashMap<>());
+    private final RemoteCallbackList<IImsFeatureStatusCallback> mStatusCallbacks =
+            new RemoteCallbackList<>();
     private @ImsState int mState = STATE_UNAVAILABLE;
     private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
     private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks =
@@ -397,9 +364,7 @@
             // If we have just connected, send queued status.
             c.notifyImsFeatureStatus(getFeatureState());
             // Add the callback if the callback completes successfully without a RemoteException.
-            synchronized (mLock) {
-                mStatusCallbacks.add(c);
-            }
+            mStatusCallbacks.register(c);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
         }
@@ -411,29 +376,21 @@
      */
     @VisibleForTesting
     public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
-        synchronized (mLock) {
-            mStatusCallbacks.remove(c);
-        }
+        mStatusCallbacks.unregister(c);
     }
 
     /**
      * Internal method called by ImsFeature when setFeatureState has changed.
      */
     private void notifyFeatureState(@ImsState int state) {
-        synchronized (mLock) {
-            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
-                    iter.hasNext(); ) {
-                IImsFeatureStatusCallback callback = iter.next();
-                try {
-                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
-                    callback.notifyImsFeatureStatus(state);
-                } catch (RemoteException e) {
-                    // remove if the callback is no longer alive.
-                    iter.remove();
-                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
-                }
+        mStatusCallbacks.broadcast((c) -> {
+            try {
+                c.notifyImsFeatureStatus(state);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " notifyFeatureState() - Skipping "
+                        + "callback.");
             }
-        }
+        });
     }
 
     /**
@@ -452,10 +409,23 @@
     /**
      * @hide
      */
-    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+    final void removeCapabilityCallback(IImsCapabilityCallback c) {
         mCapabilityCallbacks.unregister(c);
     }
 
+    /**@hide*/
+    final void queryCapabilityConfigurationInternal(int capability, int radioTech,
+            IImsCapabilityCallback c) {
+        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+        try {
+            if (c != null) {
+                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+        }
+    }
+
     /**
      * @return the cached capabilities status for this feature.
      * @hide
@@ -484,31 +454,36 @@
     /**
      * Called by the ImsFeature when the capabilities status has changed.
      *
-     * @param c A {@link Capabilities} containing the new Capabilities status.
+     * @param caps the new {@link Capabilities} status of the {@link ImsFeature}.
      *
      * @hide
      */
-    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+    protected final void notifyCapabilitiesStatusChanged(Capabilities caps) {
         synchronized (mLock) {
-            mCapabilityStatus = c.copy();
+            mCapabilityStatus = caps.copy();
         }
-        int count = mCapabilityCallbacks.beginBroadcast();
-        try {
-            for (int i = 0; i < count; i++) {
-                try {
-                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
-                            c.mCapabilities);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
-                            "callback.");
-                }
+        mCapabilityCallbacks.broadcast((callback) -> {
+            try {
+                callback.onCapabilitiesStatusChanged(caps.mCapabilities);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " notifyCapabilitiesStatusChanged() - Skipping "
+                        + "callback.");
             }
-        } finally {
-            mCapabilityCallbacks.finishBroadcast();
-        }
+        });
     }
 
     /**
+     * Provides the ImsFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     * @hide
+     */
+    public abstract boolean queryCapabilityConfiguration(int capability, int radioTech);
+
+    /**
      * Features should override this method to receive Capability preference change requests from
      * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
      * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 898ca48..056a0ab 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -37,7 +37,6 @@
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.ImsSmsImplBase;
 import android.telephony.ims.stub.ImsUtImplBase;
-import android.util.Log;
 
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsEcbm;
@@ -154,17 +153,13 @@
         @Override
         public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
                 IImsCapabilityCallback c) {
-            synchronized (mLock) {
-                MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
-            }
+            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
         }
 
         @Override
         public void queryCapabilityConfiguration(int capability, int radioTech,
                 IImsCapabilityCallback c) {
-            synchronized (mLock) {
-                queryCapabilityConfigurationInternal(capability, radioTech, c);
-            }
+            queryCapabilityConfigurationInternal(capability, radioTech, c);
         }
 
         @Override
@@ -381,18 +376,6 @@
         }
     }
 
-    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
-            IImsCapabilityCallback c) {
-        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
-        try {
-            if (c != null) {
-                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
-            }
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
-        }
-    }
-
     /**
      * The current capability status that this MmTelFeature has defined is available. This
      * configuration will be used by the platform to figure out which capabilities are CURRENTLY
@@ -512,6 +495,7 @@
      * @param capability The capability that we are querying the configuration for.
      * @return true if the capability is enabled, false otherwise.
      */
+    @Override
     public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
             @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
         // Base implementation - Override to provide functionality
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 5fae3ee..f69b434 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -16,14 +16,32 @@
 
 package android.telephony.ims.feature;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
 import android.telephony.ims.aidl.IImsRcsFeature;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.telephony.ims.stub.RcsPresenceExchangeImplBase;
 import android.telephony.ims.stub.RcsSipOptionsImplBase;
+import android.util.Log;
+
+import com.android.internal.util.FunctionalUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 
 /**
  * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
@@ -33,10 +51,132 @@
 @SystemApi
 public class RcsFeature extends ImsFeature {
 
-    /**{@inheritDoc}*/
-    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
-        // Empty Default Implementation.
-    };
+    private static final String LOG_TAG = "RcsFeature";
+
+    private static final class RcsFeatureBinder extends IImsRcsFeature.Stub {
+        // Reference the outer class in order to have better test coverage metrics instead of
+        // creating a inner class referencing the outer class directly.
+        private final RcsFeature mReference;
+        private final Executor mExecutor;
+
+        RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
+            mReference = classRef;
+            mExecutor = executor;
+        }
+
+        @Override
+        public void setListener(IRcsFeatureListener listener) {
+            mReference.setListener(listener);
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            return executeMethodAsyncForResult(
+                    () -> mReference.queryCapabilityStatus().mCapabilities,
+                    "queryCapabilityStatus");
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.addCapabilityCallback(c), "addCapabilityCallback");
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.removeCapabilityCallback(c),
+                    "removeCapabilityCallback");
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest r,
+                IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.requestChangeEnabledCapabilities(r, c),
+                    "changeCapabilitiesConfiguration");
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) throws RemoteException {
+            executeMethodAsync(() -> mReference.queryCapabilityConfigurationInternal(capability,
+                    radioTech, c), "queryCapabilityConfiguration");
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            return executeMethodAsyncForResult(mReference::getFeatureState, "getFeatureState");
+        }
+
+        // RcsPresenceExchangeImplBase specific APIS
+        @Override
+        public void requestCapabilities(List<Uri> uris, int operationToken) throws RemoteException {
+            executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+                    .requestCapabilities(uris, operationToken), "requestCapabilities");
+        }
+        @Override
+        public void updateCapabilities(RcsContactUceCapability capabilities, int operationToken)
+                throws RemoteException {
+            executeMethodAsync(() -> mReference.getPresenceExchangeInternal()
+                            .updateCapabilities(capabilities, operationToken),
+                    "updateCapabilities");
+
+        }
+        // RcsSipOptionsImplBase specific APIS
+        @Override
+        public void sendCapabilityRequest(Uri contactUri, RcsContactUceCapability capabilities,
+                int operationToken) throws RemoteException {
+            executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+                            .sendCapabilityRequest(contactUri, capabilities, operationToken),
+                    "sendCapabilityRequest");
+
+        }
+        @Override
+        public void respondToCapabilityRequest(String contactUri,
+                RcsContactUceCapability ownCapabilities, int operationToken)
+                throws RemoteException {
+            executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+                            .respondToCapabilityRequest(contactUri, ownCapabilities,
+                                    operationToken), "respondToCapabilityRequest");
+
+        }
+        @Override
+        public void respondToCapabilityRequestWithError(Uri contactUri, int code, String reason,
+                int operationToken) throws RemoteException {
+            executeMethodAsync(() -> mReference.getOptionsExchangeInternal()
+                            .respondToCapabilityRequestWithError(contactUri, code, reason,
+                                    operationToken), "respondToCapabilityRequestWithError");
+        }
+
+        // Call the methods with a clean calling identity on the executor and wait indefinitely for
+        // the future to return.
+        private void executeMethodAsync(FunctionalUtils.ThrowingRunnable r, String errorLogName)
+                throws RemoteException {
+            // call with a clean calling identity on the executor and wait indefinitely for the
+            // future to return.
+            try {
+                CompletableFuture.runAsync(
+                        () -> Binder.withCleanCallingIdentity(r), mExecutor).join();
+            } catch (CancellationException | CompletionException e) {
+                Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+
+        private <T> T executeMethodAsyncForResult(FunctionalUtils.ThrowingSupplier<T> r,
+                String errorLogName) throws RemoteException {
+            // call with a clean calling identity on the executor and wait indefinitely for the
+            // future to return.
+            CompletableFuture<T> future = CompletableFuture.supplyAsync(
+                    () -> Binder.withCleanCallingIdentity(r), mExecutor);
+            try {
+                return future.get();
+            } catch (ExecutionException | InterruptedException e) {
+                Log.w(LOG_TAG, "RcsFeatureBinder - " + errorLogName + " exception: "
+                        + e.getMessage());
+                throw new RemoteException(e.getMessage());
+            }
+        }
+    }
 
     /**
      * Contains the capabilities defined and supported by a {@link RcsFeature} in the
@@ -81,27 +221,66 @@
 
         /**@hide*/
         public RcsImsCapabilities(@RcsImsCapabilityFlag int capabilities) {
+            super(capabilities);
+        }
 
+        /**@hide*/
+        private RcsImsCapabilities(Capabilities c) {
+            super(c.getMask());
         }
 
         /**@hide*/
         @Override
         public void addCapabilities(@RcsImsCapabilityFlag int capabilities) {
-
+            super.addCapabilities(capabilities);
         }
 
         /**@hide*/
         @Override
         public void removeCapabilities(@RcsImsCapabilityFlag int capabilities) {
-
+            super.removeCapabilities(capabilities);
         }
 
         /**@hide*/
         @Override
         public boolean isCapable(@RcsImsCapabilityFlag int capabilities) {
-            return false;
+            return super.isCapable(capabilities);
         }
     }
+
+    private final RcsFeatureBinder mImsRcsBinder;
+    private IRcsFeatureListener mListenerBinder;
+    private RcsPresenceExchangeImplBase mPresExchange;
+    private RcsSipOptionsImplBase mSipOptions;
+
+    /**
+     * Create a new RcsFeature.
+     * <p>
+     * Method stubs called from the framework will be called asynchronously. To specify the
+     * {@link Executor} that the methods stubs will be called, use
+     * {@link RcsFeature#RcsFeature(Executor)} instead.
+     */
+    public RcsFeature() {
+        super();
+        // Run on the Binder threads that call them.
+        mImsRcsBinder = new RcsFeatureBinder(this, Runnable::run);
+    }
+
+    /**
+     * Create a new RcsFeature using the Executor specified for methods being called by the
+     * framework.
+     * @param executor The executor for the framework to use when making calls to this service.
+     * @hide
+     */
+    public RcsFeature(@NonNull Executor executor) {
+        super();
+        if (executor == null) {
+            throw new IllegalArgumentException("executor can not be null.");
+        }
+        // Run on the Binder thread by default.
+        mImsRcsBinder = new RcsFeatureBinder(this, executor);
+    }
+
     /**
      * Query the current {@link RcsImsCapabilities} status set by the RcsFeature. If a capability is
      * set, the {@link RcsFeature} has brought up the capability and is ready for framework
@@ -111,7 +290,7 @@
      */
     @Override
     public final RcsImsCapabilities queryCapabilityStatus() {
-        throw new UnsupportedOperationException();
+        return new RcsImsCapabilities(super.queryCapabilityStatus());
     }
 
     /**
@@ -120,8 +299,11 @@
      * Call {@link #queryCapabilityStatus()} to return the current capability status.
      * @hide
      */
-    public final void notifyCapabilitiesStatusChanged(RcsImsCapabilities c) {
-        throw new UnsupportedOperationException();
+    public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
+        if (c == null) {
+            throw new IllegalArgumentException("RcsImsCapabilities must be non-null!");
+        }
+        super.notifyCapabilitiesStatusChanged(c);
     }
 
     /**
@@ -133,8 +315,10 @@
      * @hide
      */
     public boolean queryCapabilityConfiguration(
-            @RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
-        throw new UnsupportedOperationException();
+            @RcsImsCapabilities.RcsImsCapabilityFlag int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base Implementation - Override to provide functionality
+        return false;
     }
     /**
      * Called from the framework when the {@link RcsImsCapabilities} that have been configured for
@@ -155,7 +339,7 @@
     @Override
     public void changeEnabledCapabilities(CapabilityChangeRequest request,
             CapabilityCallbackProxy c) {
-        throw new UnsupportedOperationException();
+        // Base Implementation - Override to provide functionality
     }
 
     /**
@@ -192,13 +376,6 @@
         return new RcsPresenceExchangeImplBase();
     }
 
-    /**
-     * Construct a new {@link RcsFeature} instance.
-     */
-    public RcsFeature() {
-        super();
-    }
-
     /**{@inheritDoc}*/
     @Override
     public void onFeatureRemoved() {
@@ -218,4 +395,40 @@
     public final IImsRcsFeature getBinder() {
         return mImsRcsBinder;
     }
+
+    /**@hide*/
+    public IRcsFeatureListener getListener() {
+        synchronized (mLock) {
+            return mListenerBinder;
+        }
+    }
+
+    private void setListener(IRcsFeatureListener listener) {
+        synchronized (mLock) {
+            mListenerBinder = listener;
+            if (mListenerBinder != null) {
+                onFeatureReady();
+            }
+        }
+    }
+
+    private RcsPresenceExchangeImplBase getPresenceExchangeInternal() {
+        synchronized (mLock) {
+            if (mPresExchange == null) {
+                mPresExchange = getPresenceExchangeImpl();
+                mPresExchange.initialize(this);
+            }
+            return mPresExchange;
+        }
+    }
+
+    private RcsSipOptionsImplBase getOptionsExchangeInternal() {
+        synchronized (mLock) {
+            if (mSipOptions == null) {
+                mSipOptions = getOptionsExchangeImpl();
+                mSipOptions.initialize(this);
+            }
+            return mSipOptions;
+        }
+    }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
index 289fd4c..fda295a 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -17,6 +17,11 @@
 package android.telephony.ims.stub;
 
 import android.annotation.IntDef;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.aidl.IRcsFeatureListener;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -72,6 +77,24 @@
     })
     public @interface CommandCode {}
 
+
+    private RcsFeature mFeature;
+
+    /** @hide */
+    public final void initialize(RcsFeature feature) {
+        mFeature = feature;
+    }
+
+    /** @hide */
+    protected final IRcsFeatureListener getListener() throws ImsException {
+        IRcsFeatureListener listener = mFeature.getListener();
+        if (listener == null) {
+            throw new ImsException("Connection to Framework has not been established, wait for "
+                    + "onFeatureReady().", ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
+        return mFeature.getListener();
+    }
+
     /**
      * Provides the framework with an update as to whether or not a command completed successfully
      * locally. This includes capabilities requests and updates from the network. If it does not
@@ -82,8 +105,18 @@
      * @param code The result of the pending command. If {@link #COMMAND_CODE_SUCCESS}, further
      *             updates will be sent for this command using the associated operationToken.
      * @param operationToken the token associated with the pending command.
+     * @throws ImsException If this {@link RcsCapabilityExchange} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
-    public final void onCommandUpdate(@CommandCode int code, int operationToken) {
-        throw new UnsupportedOperationException();
+    public final void onCommandUpdate(@CommandCode int code, int operationToken)
+            throws ImsException {
+        try {
+            getListener().onCommandUpdate(code, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
index 4402470..055fca5 100644
--- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -19,7 +19,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -113,54 +117,95 @@
      * Provide the framework with a subsequent network response update to
      * {@link #updateCapabilities(RcsContactUceCapability, int)} and
      * {@link #requestCapabilities(List, int)} operations.
+     *
      * @param code The SIP response code sent from the network for the operation token specified.
      * @param reason The optional reason response from the network. If the network provided no
      *         reason with the code, the string should be empty.
      * @param operationToken The token associated with the operation this service is providing a
      *         response for.
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onNetworkResponse(@PresenceResponseCode int code, @NonNull String reason,
-            int operationToken) {
-        throw new UnsupportedOperationException();
+            int operationToken) throws ImsException {
+        try {
+            getListener().onNetworkResponse(code, reason, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
      * Provides the framework with the requested contacts’ capabilities requested by the framework
-     * using {@link #requestCapabilities(List, int)} .
+     * using {@link #requestCapabilities(List, int)}.
+     *
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onCapabilityRequestResponse(@NonNull List<RcsContactUceCapability> infos,
-            int operationToken) {
-        throw new UnsupportedOperationException();
+            int operationToken) throws ImsException {
+        try {
+            getListener().onCapabilityRequestResponsePresence(infos, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
      * Trigger the framework to provide a capability update using
-     * {@link #updateCapabilities(RcsContactUceCapability, int)}. This is typically used when trying
-     * to generate an initial PUBLISH for a new subscription to the network.
+     * {@link #updateCapabilities(RcsContactUceCapability, int)}.
      * <p>
-     * The device will cache all presence publications after boot until this method is called once.
+     * This is typically used when trying to generate an initial PUBLISH for a new subscription to
+     * the network. The device will cache all presence publications after boot until this method is
+     * called once.
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
-    public final void onNotifyUpdateCapabilites() {
-        throw new UnsupportedOperationException();
+    public final void onNotifyUpdateCapabilites() throws ImsException {
+        try {
+            getListener().onNotifyUpdateCapabilities();
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
      * Notify the framework that the device’s capabilities have been unpublished from the network.
+     *
+     * @throws ImsException If this {@link RcsPresenceExchangeImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
-    public final void onUnpublish() {
-        throw new UnsupportedOperationException();
+    public final void onUnpublish() throws ImsException {
+        try {
+            getListener().onUnpublish();
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
-     * The user capabilities of one or multiple contacts have been requested.
+     * The user capabilities of one or multiple contacts have been requested by the framework.
      * <p>
-     * This must be followed up with one call to {@link #onCommandUpdate(int, int)} with an update
-     * as to whether or not the command completed as well as subsequent network
-     * updates using {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
-     * {@link #onCapabilityRequestResponse(List, int)}  should be called with
-     * the presence information for the contacts specified.
-     * @param uris A {@link List} of the URIs that the framework is requesting the UCE capabilities
-     *          for.
+     * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation succeeded.  If this operation succeeds, network
+     * response updates should be sent to the framework using
+     * {@link #onNetworkResponse(int, String, int)}. When the operation is completed,
+     * {@link #onCapabilityRequestResponse(List, int)} should be called with the presence
+     * information for the contacts specified.
+     * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
+     *             capabilities for.
      * @param operationToken The token associated with this operation. Updates to this request using
      *         {@link #onCommandUpdate(int, int)}, {@link #onNetworkResponse(int, String, int)}, and
      *         {@link #onCapabilityRequestResponse(List, int)}  must use the same operation token
@@ -169,14 +214,20 @@
     public void requestCapabilities(@NonNull List<Uri> uris, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "requestCapabilities called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 
     /**
-     * The capabilities of this device have been updated and should be published
-     * to the network. The framework will expect one {@link #onCommandUpdate(int, int)} call to
-     * indicate whether or not this operation failed first as well as network response
-     * updates to this update using {@link #onNetworkResponse(int, String, int)}.
+     * The capabilities of this device have been updated and should be published to the network.
+     * <p>
+     * The implementer must follow up this call with an {@link #onCommandUpdate(int, int)} call to
+     * indicate whether or not this operation succeeded. If this operation succeeds, network
+     * response updates should be sent to the framework using
+     * {@link #onNetworkResponse(int, String, int)}.
      * @param capabilities The capabilities for this device.
      * @param operationToken The token associated with this operation. Any subsequent
      *         {@link #onCommandUpdate(int, int)} or {@link #onNetworkResponse(int, String, int)}
@@ -186,6 +237,10 @@
             int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "updateCapabilities called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
index 3343074..1c68fc0 100644
--- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -20,7 +20,11 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.Uri;
+import android.os.RemoteException;
+import android.telephony.ims.ImsException;
 import android.telephony.ims.RcsContactUceCapability;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.RcsFeature;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -87,10 +91,19 @@
      * @param info the contact's UCE capabilities associated with the capability request.
      * @param operationToken The token associated with the original capability request, set by
      *        {@link #sendCapabilityRequest(Uri, RcsContactUceCapability, int)}.
+     * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onCapabilityRequestResponse(@SipResponseCode int code, @NonNull String reason,
-            @Nullable RcsContactUceCapability info, int operationToken) {
-        throw new UnsupportedOperationException();
+            @Nullable RcsContactUceCapability info, int operationToken) throws ImsException {
+        try {
+            getListener().onCapabilityRequestResponseOptions(code, reason, info, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
@@ -104,10 +117,19 @@
      * @param operationToken An unique operation token that you have generated that will be returned
      *         by the framework in
      *         {@link #respondToCapabilityRequest(String, RcsContactUceCapability, int)}.
+     * @throws ImsException If this {@link RcsSipOptionsImplBase} instance is not currently
+     * connected to the framework. This can happen if the {@link RcsFeature} is not
+     * {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received the
+     * {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases when the
+     * Telephony stack has crashed.
      */
     public final void onRemoteCapabilityRequest(@NonNull Uri contactUri,
-            @NonNull RcsContactUceCapability remoteInfo, int operationToken) {
-        throw new UnsupportedOperationException();
+            @NonNull RcsContactUceCapability remoteInfo, int operationToken) throws ImsException {
+        try {
+            getListener().onRemoteCapabilityRequest(contactUri, remoteInfo, operationToken);
+        } catch (RemoteException e) {
+            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+        }
     }
 
     /**
@@ -127,7 +149,11 @@
             @NonNull RcsContactUceCapability capabilities, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "sendCapabilityRequest called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 
     /**
@@ -145,7 +171,11 @@
             @NonNull RcsContactUceCapability ownCapabilities, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "respondToCapabilityRequest called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 
     /**
@@ -164,6 +194,10 @@
             @SipResponseCode int code, @NonNull String reason, int operationToken) {
         // Stub - to be implemented by service
         Log.w(LOG_TAG, "respondToCapabiltyRequestWithError called with no implementation.");
-        onCommandUpdate(COMMAND_CODE_GENERIC_FAILURE, operationToken);
+        try {
+            getListener().onCommandUpdate(COMMAND_CODE_NOT_SUPPORTED, operationToken);
+        } catch (RemoteException | ImsException e) {
+            // Do not do anything, this is a stub implementation.
+        }
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index e9a177d..4e1ff8f 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -32,9 +32,11 @@
 import android.os.UserManager;
 import android.provider.ContactsContract.PhoneLookup;
 import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -80,6 +82,10 @@
      * classes.
      */
     private static final class CookieWrapper {
+        @UnsupportedAppUsage
+        private CookieWrapper() {
+        }
+
         public OnQueryCompleteListener listener;
         public Object cookie;
         public int event;
@@ -527,6 +533,7 @@
     /**
      * Releases the relevant data.
      */
+    @UnsupportedAppUsage
     private void release() {
         mHandler.mContext = null;
         mHandler.mQueryUri = null;
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index cde6db4..e1113eb 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -17,6 +17,8 @@
 
 import com.android.internal.util.Protocol;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 /**
  * @hide
  */
@@ -37,20 +39,34 @@
      *      RETRYING or CONNECTING: CONNECTING
      *      CONNECTED : CONNECTED or DISCONNECTING
      */
+    @UnsupportedAppUsage(implicitMember =
+            "values()[Lcom/android/internal/telephony/DctConstants$State;")
     public enum State {
+        @UnsupportedAppUsage
         IDLE,
+        @UnsupportedAppUsage
         CONNECTING,
+        @UnsupportedAppUsage
         RETRYING,
+        @UnsupportedAppUsage
         CONNECTED,
+        @UnsupportedAppUsage
         DISCONNECTING,
+        @UnsupportedAppUsage
         FAILED,
     }
 
+    @UnsupportedAppUsage(implicitMember =
+            "values()[Lcom/android/internal/telephony/DctConstants$Activity;")
     public enum Activity {
         NONE,
+        @UnsupportedAppUsage
         DATAIN,
+        @UnsupportedAppUsage
         DATAOUT,
+        @UnsupportedAppUsage
         DATAINANDOUT,
+        @UnsupportedAppUsage
         DORMANT
     }
 
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index a75096f..5fb4e90 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -16,19 +16,17 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.res.Resources;
+import android.os.Build;
+import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.SparseIntArray;
 
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.telephony.Rlog;
+import com.android.internal.R;
 
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
-import com.android.internal.telephony.SmsConstants;
-import com.android.internal.R;
-
 import java.util.ArrayList;
 import java.util.List;
 
@@ -83,6 +81,11 @@
      * data.
      */
     public static class TextEncodingDetails {
+
+        @UnsupportedAppUsage
+        public TextEncodingDetails() {
+        }
+
         /**
          *The number of SMS's required to encode the text.
          */
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 5b509b7..15e7fc2 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -27,6 +27,7 @@
     /**
      * Retrieves the unique device ID, e.g., IMEI for GSM phones.
      */
+    @UnsupportedAppUsage
     String getDeviceId(String callingPackage);
 
      /**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a65acac..9f1a2f7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -94,6 +94,7 @@
      * @param callingPackage the name of the package making the call.
      * @return returns true if the radio is on.
      */
+    @UnsupportedAppUsage
     boolean isRadioOn(String callingPackage);
 
     /**
@@ -1254,6 +1255,7 @@
       * <p>Requires Permission:
       *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
       */
+    @UnsupportedAppUsage
     String getDeviceId(String callingPackage);
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index d57f9af..6ff27b1 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -17,6 +17,8 @@
 
 import android.telephony.TelephonyManager;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 /**
  * {@hide}
  */
@@ -65,15 +67,26 @@
      *
      * The ordinal values much match {@link TelephonyManager#SIM_STATE_UNKNOWN} ...
      */
+    @UnsupportedAppUsage(implicitMember =
+            "values()[Lcom/android/internal/telephony/IccCardConstants$State;")
     public enum State {
+        @UnsupportedAppUsage
         UNKNOWN,        /** ordinal(0) == {@See TelephonyManager#SIM_STATE_UNKNOWN} */
+        @UnsupportedAppUsage
         ABSENT,         /** ordinal(1) == {@See TelephonyManager#SIM_STATE_ABSENT} */
+        @UnsupportedAppUsage
         PIN_REQUIRED,   /** ordinal(2) == {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} */
+        @UnsupportedAppUsage
         PUK_REQUIRED,   /** ordinal(3) == {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} */
+        @UnsupportedAppUsage
         NETWORK_LOCKED, /** ordinal(4) == {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} */
+        @UnsupportedAppUsage
         READY,          /** ordinal(5) == {@See TelephonyManager#SIM_STATE_READY} */
+        @UnsupportedAppUsage
         NOT_READY,      /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
+        @UnsupportedAppUsage
         PERM_DISABLED,  /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
+        @UnsupportedAppUsage
         CARD_IO_ERROR,  /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
         CARD_RESTRICTED,/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
         LOADED;         /** ordinal(9) == {@See TelephonyManager#SIM_STATE_LOADED} */
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index d5061a3..ee1a476 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -15,6 +15,8 @@
  */
 package com.android.internal.telephony;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 /**
  * @hide
  */
@@ -31,8 +33,12 @@
      * ringing or waiting.</li>
      * </ul>
      */
+    @UnsupportedAppUsage(implicitMember =
+            "values()[Lcom/android/internal/telephony/PhoneConstants$State;")
     public enum State {
-        IDLE, RINGING, OFFHOOK;
+        @UnsupportedAppUsage IDLE,
+        @UnsupportedAppUsage RINGING,
+        @UnsupportedAppUsage OFFHOOK;
     };
 
     /**
@@ -46,8 +52,13 @@
       *                 in 2G network</li>
       * </ul>
       */
+    @UnsupportedAppUsage(implicitMember =
+            "values()[Lcom/android/internal/telephony/PhoneConstants$DataState;")
     public enum DataState {
-        CONNECTED, CONNECTING, DISCONNECTED, SUSPENDED;
+        @UnsupportedAppUsage CONNECTED,
+        @UnsupportedAppUsage CONNECTING,
+        @UnsupportedAppUsage DISCONNECTED,
+        @UnsupportedAppUsage SUSPENDED;
     };
 
     public static final String STATE_KEY = "state";
@@ -69,9 +80,13 @@
     public static final int LTE_ON_CDMA_TRUE = RILConstants.LTE_ON_CDMA_TRUE;
 
     // Number presentation type for caller id display (From internal/Connection.java)
+    @UnsupportedAppUsage
     public static final int PRESENTATION_ALLOWED = 1;    // normal
+    @UnsupportedAppUsage
     public static final int PRESENTATION_RESTRICTED = 2; // block by user
+    @UnsupportedAppUsage
     public static final int PRESENTATION_UNKNOWN = 3;    // no specified or unknown by network
+    @UnsupportedAppUsage
     public static final int PRESENTATION_PAYPHONE = 4;   // show pay phone info
 
     // Sim activation type
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 5205973..03ea920 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -18,6 +18,8 @@
 
 import android.telephony.TelephonyManager;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 /**
  * {@hide}
  */
@@ -230,6 +232,7 @@
     /** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
     int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
 
+    @UnsupportedAppUsage
     int PREFERRED_NETWORK_MODE = Integer.parseInt(TelephonyManager.getTelephonyProperty(0,
             "ro.telephony.default_network", Integer.toString(NETWORK_MODE_WCDMA_PREF)));
 
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 49c737f..2cdf2f6 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -16,27 +16,28 @@
 
 package com.android.internal.telephony;
 
-import android.telephony.Rlog;
-import android.os.Build;
-import android.util.SparseIntArray;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
-import android.telephony.SmsManager;
-import android.telephony.TelephonyManager;
+import android.os.Build;
+import android.telephony.Rlog;
+import android.util.SparseIntArray;
 
-import com.android.internal.util.XmlUtils;
 import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.util.XmlUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import dalvik.annotation.compat.UnsupportedAppUsage;
 
 public class Sms7BitEncodingTranslator {
     private static final String TAG = "Sms7BitEncodingTranslator";
+    @UnsupportedAppUsage
     private static final boolean DBG = Build.IS_DEBUGGABLE ;
     private static boolean mIs7BitTranslationTableLoaded = false;
     private static SparseIntArray mTranslationTable = null;
+    @UnsupportedAppUsage
     private static SparseIntArray mTranslationTableCommon = null;
+    @UnsupportedAppUsage
     private static SparseIntArray mTranslationTableGSM = null;
+    @UnsupportedAppUsage
     private static SparseIntArray mTranslationTableCDMA = null;
 
     // Parser variables
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 62d3536..f10398f 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -48,6 +48,8 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -91,6 +93,7 @@
         /**
          * Name of this SMS app for display.
          */
+        @UnsupportedAppUsage
         private String mApplicationName;
 
         /**
@@ -191,7 +194,7 @@
      * @return
      */
     private static int getIncomingUserId(Context context) {
-        int contextUserId = context.getUserId();
+        int contextUserId = UserHandle.myUserId();
         final int callingUid = Binder.getCallingUid();
         if (DEBUG_MULTIUSER) {
             Log.i(LOG_TAG, "getIncomingUserHandle caller=" + callingUid + ", myuid="
@@ -224,6 +227,7 @@
      * Implement ACTION_SENDTO intent.
      * Support smsto Uri scheme.
      */
+    @UnsupportedAppUsage
     public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
         return getApplicationCollectionAsUser(context, getIncomingUserId(context));
     }
@@ -425,9 +429,6 @@
             final SmsApplicationData smsApplicationData = receivers.get(packageName);
             if (smsApplicationData != null) {
                 if (!smsApplicationData.isComplete()) {
-                    Log.w(LOG_TAG, "Package " + packageName
-                            + " lacks required manifest declarations to be a default sms app: "
-                            + smsApplicationData);
                     receivers.remove(packageName);
                 }
             }
@@ -579,6 +580,7 @@
      * Sets the specified package as the default SMS/MMS application. The caller of this method
      * needs to have permission to set AppOps and write to secure settings.
      */
+    @UnsupportedAppUsage
     public static void setDefaultApplication(String packageName, Context context) {
         setDefaultApplicationAsUser(packageName, context, getIncomingUserId(context));
     }
@@ -826,6 +828,7 @@
         sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL, false);
     }
 
+    @UnsupportedAppUsage
     private static void configurePreferredActivity(PackageManager packageManager,
             ComponentName componentName, int userId) {
         // Add the four activity preferences we want to direct to this app.
@@ -868,6 +871,7 @@
      * Returns SmsApplicationData for this package if this package is capable of being set as the
      * default SMS application.
      */
+    @UnsupportedAppUsage
     public static SmsApplicationData getSmsApplicationData(String packageName, Context context) {
         Collection<SmsApplicationData> applications = getApplicationCollection(context);
         return getApplicationForPackage(applications, packageName);
@@ -879,6 +883,7 @@
      * @param updateIfNeeded update the default app if there is no valid default app configured.
      * @return component name of the app and class to deliver SMS messages to
      */
+    @UnsupportedAppUsage
     public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
         return getDefaultSmsApplicationAsUser(context, updateIfNeeded, getIncomingUserId(context));
     }
@@ -913,6 +918,7 @@
      * @param updateIfNeeded update the default app if there is no valid default app configured.
      * @return component name of the app and class to deliver MMS messages to
      */
+    @UnsupportedAppUsage
     public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) {
         int userId = getIncomingUserId(context);
         final long token = Binder.clearCallingIdentity();
@@ -936,6 +942,7 @@
      * @param updateIfNeeded update the default app if there is no valid default app configured.
      * @return component name of the app and class to direct Respond Via Message intent to
      */
+    @UnsupportedAppUsage
     public static ComponentName getDefaultRespondViaMessageApplication(Context context,
             boolean updateIfNeeded) {
         int userId = getIncomingUserId(context);
@@ -1036,6 +1043,7 @@
      * <p>
      * Caller must pass in the correct user context if calling from a singleton service.
      */
+    @UnsupportedAppUsage
     public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
         if (SmsManager.getDefault().getAutoPersisting()) {
             return true;
@@ -1050,6 +1058,7 @@
      * @param packageName the name of the package to be checked
      * @return true if the package is default sms app or bluetooth
      */
+    @UnsupportedAppUsage
     public static boolean isDefaultSmsApplication(Context context, String packageName) {
         if (packageName == null) {
             return false;
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index 9fe1718..dd77b01 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -16,13 +16,12 @@
 
 package com.android.internal.telephony;
 
-import com.android.internal.telephony.SmsConstants;
+import android.annotation.UnsupportedAppUsage;
+
 import com.android.internal.util.HexDump;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-
-import android.annotation.UnsupportedAppUsage;
 import java.util.ArrayList;
 
 /**
@@ -74,6 +73,10 @@
 
     public static class PortAddrs {
         @UnsupportedAppUsage
+        public PortAddrs() {
+        }
+
+        @UnsupportedAppUsage
         public int destPort;
         @UnsupportedAppUsage
         public int origPort;
@@ -82,6 +85,10 @@
 
     public static class ConcatRef {
         @UnsupportedAppUsage
+        public ConcatRef() {
+        }
+
+        @UnsupportedAppUsage
         public int refNumber;
         @UnsupportedAppUsage
         public int seqNumber;
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index ffdc4b6..f0687b4 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,18 +16,17 @@
 
 package com.android.internal.telephony;
 
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SmsHeader;
-import java.text.BreakIterator;
-import java.util.Arrays;
-
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.provider.Telephony;
 import android.telephony.SmsMessage;
 import android.text.Emoji;
 
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+
+import java.text.BreakIterator;
+import java.util.Arrays;
+
 /**
  * Base class declaring the specific methods and members for SmsMessage.
  * {@hide}
@@ -102,6 +101,10 @@
     @UnsupportedAppUsage
     public int mMessageRef;
 
+    @UnsupportedAppUsage
+    public SmsMessageBase() {
+    }
+
     // TODO(): This class is duplicated in SmsMessage.java. Refactor accordingly.
     public static abstract class SubmitPduBase  {
         @UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index dd9b242..ef485071 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 /**
  * Contains a list of string constants used to get or set telephone properties
  * in the system. You can use {@link android.os.SystemProperties os.SystemProperties}
@@ -101,6 +103,7 @@
      *  provider of the SIM. 5 or 6 decimal digits.
      *  Availability: SIM state must be "READY"
      */
+    @UnsupportedAppUsage
     static String PROPERTY_ICC_OPERATOR_NUMERIC = "gsm.sim.operator.numeric";
 
     /** PROPERTY_ICC_OPERATOR_ALPHA is also known as the SPN, or Service Provider Name.
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index e2a8913b..6b3126d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -19,17 +19,17 @@
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
 
 import android.content.res.Resources;
-import android.os.Parcel;
 import android.os.SystemProperties;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
 import android.telephony.cdma.CdmaSmsCbProgramData;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
 import com.android.internal.telephony.SmsAddress;
 import com.android.internal.telephony.SmsConstants;
 import com.android.internal.telephony.SmsHeader;
@@ -43,7 +43,8 @@
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.HexDump;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
 
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -109,7 +110,9 @@
     private static final int PRIORITY_URGENT        = 0x2;
     private static final int PRIORITY_EMERGENCY     = 0x3;
 
+    @UnsupportedAppUsage
     private SmsEnvelope mEnvelope;
+    @UnsupportedAppUsage
     private BearerData mBearerData;
 
     /** @hide */
@@ -119,15 +122,20 @@
         createPdu();
     }
 
+    @UnsupportedAppUsage
     public SmsMessage() {}
 
     public static class SubmitPdu extends SubmitPduBase {
+        @UnsupportedAppUsage
+        public SubmitPdu() {
+        }
     }
 
     /**
      * Create an SmsMessage from a raw PDU.
      * Note: In CDMA the PDU is just a byte representation of the received Sms.
      */
+    @UnsupportedAppUsage
     public static SmsMessage createFromPdu(byte[] pdu) {
         SmsMessage msg = new SmsMessage();
 
@@ -153,6 +161,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static SmsMessage createFromEfRecord(int index, byte[] data) {
         try {
             SmsMessage msg = new SmsMessage();
@@ -219,6 +228,7 @@
      *         Returns null on encode error.
      * @hide
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
             boolean statusReportRequested, SmsHeader smsHeader) {
         return getSubmitPdu(scAddr, destAddr, message, statusReportRequested, smsHeader, -1);
@@ -239,6 +249,7 @@
      *         Returns null on encode error.
      * @hide
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
             boolean statusReportRequested, SmsHeader smsHeader, int priority) {
 
@@ -269,6 +280,7 @@
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
             byte[] data, boolean statusReportRequested) {
 
@@ -306,6 +318,7 @@
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
             boolean statusReportRequested) {
         return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
@@ -322,6 +335,7 @@
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
             boolean statusReportRequested, int priority) {
         return privateGetSubmitPdu(destAddr, statusReportRequested, userData, priority);
@@ -393,6 +407,7 @@
     }
 
     /** Return true iff the bearer data message type is DELIVERY_ACK. */
+    @UnsupportedAppUsage
     @Override
     public boolean isStatusReportMessage() {
         return (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK);
@@ -415,6 +430,7 @@
      * @param isEntireMsg indicates if this is entire msg or a segment in multipart msg
      * @return TextEncodingDetails
      */
+    @UnsupportedAppUsage
     public static TextEncodingDetails calculateLength(CharSequence messageBody,
             boolean use7bitOnly, boolean isEntireMsg) {
         CharSequence newMsgBody = null;
@@ -437,6 +453,7 @@
      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN},
      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP}
     */
+    @UnsupportedAppUsage
     public int getTeleService() {
         return mEnvelope.teleService;
     }
@@ -448,6 +465,7 @@
      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_BROADCAST},
      *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_ACKNOWLEDGE},
     */
+    @UnsupportedAppUsage
     public int getMessageType() {
         // NOTE: mEnvelope.messageType is not set correctly for cell broadcasts with some RILs.
         // Use the service category parameter to detect CMAS and other cell broadcast messages.
@@ -680,6 +698,7 @@
     /**
      * Parses a SMS message from its BearerData stream.
      */
+    @UnsupportedAppUsage
     public void parseSms() {
         // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
         // It contains only an 8-bit number with the number of messages waiting
@@ -816,6 +835,7 @@
      * binder-call, and hence should be thread-safe, it has been
      * synchronized.
      */
+    @UnsupportedAppUsage
     public synchronized static int getNextMessageId() {
         // Testing and dialog with partners has indicated that
         // msgId==0 is (sometimes?) treated specially by lower levels.
@@ -840,6 +860,7 @@
      * Creates BearerData and Envelope from parameters for a Submit SMS.
      * @return byte stream for SubmitPdu.
      */
+    @UnsupportedAppUsage
     private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
             UserData userData) {
         return privateGetSubmitPdu(destAddrStr, statusReportRequested, userData, -1);
@@ -1025,6 +1046,7 @@
     /** This function  shall be called to get the number of voicemails.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getNumOfVoicemails() {
         return mBearerData.numberOfMessages;
     }
@@ -1036,6 +1058,7 @@
      * @return byte array uniquely identifying the message.
      * @hide
      */
+    @UnsupportedAppUsage
     public byte[] getIncomingSmsFingerprint() {
         ByteArrayOutputStream output = new ByteArrayOutputStream();
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/UserData.java b/telephony/java/com/android/internal/telephony/cdma/UserData.java
index f879560..7187ae4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/UserData.java
@@ -21,6 +21,8 @@
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.util.HexDump;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 public class UserData {
 
     /**
@@ -92,6 +94,7 @@
     public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
     public static final int ASCII_NL_INDEX = 0x0A;
     public static final int ASCII_CR_INDEX = 0x0D;
+    @UnsupportedAppUsage
     public static final SparseIntArray charToAscii = new SparseIntArray();
     static {
         for (int i = 0; i < ASCII_MAP.length; i++) {
@@ -101,6 +104,10 @@
         charToAscii.put('\r', ASCII_CR_INDEX);
     }
 
+    @UnsupportedAppUsage
+    public UserData() {
+    }
+
     /*
      * TODO(cleanup): Move this very generic functionality somewhere
      * more general.
@@ -131,12 +138,15 @@
     /**
      * Contains the data header of the user data
      */
+    @UnsupportedAppUsage
     public SmsHeader userDataHeader;
 
     /**
      * Contains the data encoding type for the SMS message
      */
+    @UnsupportedAppUsage
     public int msgEncoding;
+    @UnsupportedAppUsage
     public boolean msgEncodingSet = false;
 
     public int msgType;
@@ -146,13 +156,16 @@
      */
     public int paddingBits;
 
+    @UnsupportedAppUsage
     public int numFields;
 
     /**
      * Contains the user data of a SMS message
      * (See 3GPP2 C.S0015-B, v2, 4.5.2)
      */
+    @UnsupportedAppUsage
     public byte[] payload;
+    @UnsupportedAppUsage
     public String payloadStr;
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
similarity index 99%
rename from telephony/java/com/android/internal/telephony/cdma/BearerData.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 9e6f19f..2181bc4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -17,10 +17,10 @@
 package com.android.internal.telephony.cdma.sms;
 
 import android.content.res.Resources;
+import android.telephony.Rlog;
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.cdma.CdmaSmsCbProgramData;
 import android.telephony.cdma.CdmaSmsCbProgramResults;
-import android.telephony.Rlog;
 
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
@@ -31,6 +31,8 @@
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.BitwiseOutputStream;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -95,6 +97,7 @@
      * (Special rules apply for WAP-messages.)
      * (See 3GPP2 C.S0015-B, v2, 4.5.1)
      */
+    @UnsupportedAppUsage
     public int messageId;
 
     /**
@@ -106,7 +109,9 @@
     public static final int PRIORITY_URGENT        = 0x2;
     public static final int PRIORITY_EMERGENCY     = 0x3;
 
+    @UnsupportedAppUsage
     public boolean priorityIndicatorSet = false;
+    @UnsupportedAppUsage
     public int priority = PRIORITY_NORMAL;
 
     /**
@@ -144,6 +149,7 @@
     public static final int DISPLAY_MODE_USER           = 0x2;
 
     public boolean displayModeSet = false;
+    @UnsupportedAppUsage
     public int displayMode = DISPLAY_MODE_DEFAULT;
 
     /**
@@ -207,6 +213,7 @@
      * presence of a UDH in the structured data, any existing setting
      * will be overwritten.
      */
+    @UnsupportedAppUsage
     public boolean hasUserDataHeader;
 
     /**
@@ -214,6 +221,7 @@
      * (e.g. padding bits, user data, user data header, etc)
      * (See 3GPP2 C.S.0015-B, v2, 4.5.2)
      */
+    @UnsupportedAppUsage
     public UserData userData;
 
     /**
@@ -226,6 +234,10 @@
     public boolean userResponseCodeSet = false;
     public int userResponseCode;
 
+    @UnsupportedAppUsage
+    public BearerData() {
+    }
+
     /**
      * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4
      */
@@ -244,6 +256,7 @@
 
         private ZoneId mZoneId;
 
+        @UnsupportedAppUsage
         public TimeStamp() {
             mZoneId = ZoneId.systemDefault();   // 3GPP2 timestamps use the local timezone
         }
@@ -295,6 +308,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public TimeStamp msgCenterTimeStamp;
     public TimeStamp validityPeriodAbsolute;
     public TimeStamp deferredDeliveryTimeAbsolute;
@@ -383,6 +397,7 @@
 
 
     private static class CodingException extends Exception {
+        @UnsupportedAppUsage
         public CodingException(String s) {
             super(s);
         }
@@ -476,6 +491,7 @@
         outStream.skip(3);
     }
 
+    @UnsupportedAppUsage
     private static int countAsciiSeptets(CharSequence msg, boolean force) {
         int msgLen = msg.length();
         if (force) return msgLen;
@@ -518,6 +534,7 @@
         return ted;
     }
 
+    @UnsupportedAppUsage
     private static byte[] encode7bitAscii(String msg, boolean force)
         throws CodingException
     {
@@ -949,6 +966,7 @@
      *
      * @return byte array of raw encoded SMS bearer data.
      */
+    @UnsupportedAppUsage
     public static byte[] encode(BearerData bData) {
         bData.hasUserDataHeader = ((bData.userData != null) &&
                 (bData.userData.userDataHeader != null));
@@ -1200,6 +1218,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
         throws CodingException
     {
@@ -1845,6 +1864,7 @@
      * @return the number of bits to read from the stream
      * @throws CodingException if the specified encoding is not supported
      */
+    @UnsupportedAppUsage
     private static int getBitsForNumFields(int msgEncoding, int numFields)
             throws CodingException {
         switch (msgEncoding) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
similarity index 97%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index d27a758..a82b975 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -16,11 +16,11 @@
 
 package com.android.internal.telephony.cdma.sms;
 
+import android.annotation.UnsupportedAppUsage;
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.SmsAddress;
-import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.util.HexDump;
 
 public class CdmaSmsAddress extends SmsAddress {
@@ -33,6 +33,7 @@
     static public final int DIGIT_MODE_4BIT_DTMF              = 0x00;
     static public final int DIGIT_MODE_8BIT_CHAR              = 0x01;
 
+    @UnsupportedAppUsage
     public int digitMode;
 
     /**
@@ -43,6 +44,7 @@
     static public final int NUMBER_MODE_NOT_DATA_NETWORK      = 0x00;
     static public final int NUMBER_MODE_DATA_NETWORK          = 0x01;
 
+    @UnsupportedAppUsage
     public int numberMode;
 
     /**
@@ -70,6 +72,7 @@
      * This field shall be set to the number of address digits
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
      */
+    @UnsupportedAppUsage
     public int numberOfDigits;
 
     /**
@@ -83,6 +86,7 @@
     //static protected final int NUMBERING_PLAN_TELEX             = 0x4;
     //static protected final int NUMBERING_PLAN_PRIVATE           = 0x9;
 
+    @UnsupportedAppUsage
     public int numberPlan;
 
     /**
@@ -91,6 +95,7 @@
      * respectively.
      */
 
+    @UnsupportedAppUsage
     public CdmaSmsAddress(){
     }
 
@@ -194,6 +199,7 @@
      * common punctuation.  For alpha addresses, the string is cleaned
      * up by removing whitespace.
      */
+    @UnsupportedAppUsage
     public static CdmaSmsAddress parse(String address) {
         CdmaSmsAddress addr = new CdmaSmsAddress();
         addr.address = address;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index de93b57..6af174c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -17,7 +17,7 @@
 package com.android.internal.telephony.cdma.sms;
 
 
-import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
+import android.annotation.UnsupportedAppUsage;
 
 public final class SmsEnvelope {
     /**
@@ -69,6 +69,7 @@
      * or receiving the message.
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.1)
      */
+    @UnsupportedAppUsage
     public int teleService = TELESERVICE_NOT_SET;
 
     /**
@@ -76,6 +77,7 @@
      * by the SMS message.
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.2)
      */
+    @UnsupportedAppUsage
     public int serviceCategory;
 
     /**
@@ -126,8 +128,10 @@
      * encoded bearer data
      * (See 3GPP2 C.S0015-B, v2, 3.4.3.7)
      */
+    @UnsupportedAppUsage
     public byte[] bearerData;
 
+    @UnsupportedAppUsage
     public SmsEnvelope() {
         // nothing to see here
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
index bd8c83e..17f69b3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
@@ -18,10 +18,13 @@
 
 import android.telephony.PhoneNumberUtils;
 
-import java.text.ParseException;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.SmsAddress;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.text.ParseException;
+
 public class GsmSmsAddress extends SmsAddress {
 
     static final int OFFSET_ADDRESS_LENGTH = 0;
@@ -38,7 +41,7 @@
      *        (addressLength + 1) / 2"
      * @throws ParseException
      */
-
+    @UnsupportedAppUsage
     public GsmSmsAddress(byte[] data, int offset, int length) throws ParseException {
         origBytes = new byte[length];
         System.arraycopy(data, offset, origBytes, 0, length);
@@ -136,6 +139,7 @@
      * address indicating a "set" of "indicator 1" of type "voice message
      * waiting"
      */
+    @UnsupportedAppUsage
     public boolean isCphsVoiceMessageSet() {
         // 0x11 means "set" "voice message waiting" "indicator 1"
         return isCphsVoiceMessageIndicatorAddress()
@@ -148,6 +152,7 @@
      * address indicating a "clear" of "indicator 1" of type "voice message
      * waiting"
      */
+    @UnsupportedAppUsage
     public boolean isCphsVoiceMessageClear() {
         // 0x10 means "clear" "voice message waiting" "indicator 1"
         return isCphsVoiceMessageIndicatorAddress()
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 0dbc186..996edfc 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -19,6 +19,8 @@
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.SmsCbEtwsInfo;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 import java.util.Arrays;
 
 /**
@@ -74,6 +76,7 @@
     private final int mSerialNumber;
 
     /** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */
+    @UnsupportedAppUsage
     private final int mMessageIdentifier;
 
     private final int mDataCodingScheme;
@@ -90,6 +93,7 @@
     /** CMAS warning notification info. */
     private final SmsCbCmasInfo mCmasInfo;
 
+    @UnsupportedAppUsage
     public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
         if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
             throw new IllegalArgumentException("Illegal PDU");
@@ -183,14 +187,17 @@
         }
     }
 
+    @UnsupportedAppUsage
     int getGeographicalScope() {
         return mGeographicalScope;
     }
 
+    @UnsupportedAppUsage
     int getSerialNumber() {
         return mSerialNumber;
     }
 
+    @UnsupportedAppUsage
     int getServiceCategory() {
         return mMessageIdentifier;
     }
@@ -199,10 +206,12 @@
         return mDataCodingScheme;
     }
 
+    @UnsupportedAppUsage
     int getPageIndex() {
         return mPageIndex;
     }
 
+    @UnsupportedAppUsage
     int getNumberOfPages() {
         return mNrOfPages;
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 5667387..5bb818b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -16,18 +16,29 @@
 
 package com.android.internal.telephony.gsm;
 
+import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
+import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
+import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
+import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
+import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
+import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
+import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
+import static com.android.internal.telephony.SmsConstants.MessageClass;
+
+import android.content.res.Resources;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
-import android.content.res.Resources;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
 import com.android.internal.telephony.SmsHeader;
 import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
 
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
@@ -35,16 +46,6 @@
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 
-import static com.android.internal.telephony.SmsConstants.MessageClass;
-import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
-import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
-
 /**
  * A Short Message Service message.
  *
@@ -100,11 +101,18 @@
     private static final int INVALID_VALIDITY_PERIOD = -1;
 
     public static class SubmitPdu extends SubmitPduBase {
+        @UnsupportedAppUsage
+        public SubmitPdu() {}
+    }
+
+    @UnsupportedAppUsage
+    public SmsMessage() {
     }
 
     /**
      * Create an SmsMessage from a raw PDU.
      */
+    @UnsupportedAppUsage
     public static SmsMessage createFromPdu(byte[] pdu) {
         try {
             SmsMessage msg = new SmsMessage();
@@ -169,6 +177,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static SmsMessage createFromEfRecord(int index, byte[] data) {
         try {
             SmsMessage msg = new SmsMessage();
@@ -259,6 +268,7 @@
      *         Returns null on encode error.
      * @hide
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message,
             boolean statusReportRequested, byte[] header) {
@@ -281,6 +291,7 @@
      *         Returns null on encode error.
      * @hide
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message,
             boolean statusReportRequested, byte[] header, int encoding,
@@ -304,6 +315,7 @@
      *         Returns null on encode error.
      * @hide
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message,
             boolean statusReportRequested, byte[] header, int encoding,
@@ -444,6 +456,7 @@
      * @throws UnsupportedEncodingException
      * @throws EncodeException if String is too large to encode
      */
+    @UnsupportedAppUsage
     private static byte[] encodeUCS2(String message, byte[] header)
             throws UnsupportedEncodingException, EncodeException {
         byte[] userData, textPart;
@@ -478,6 +491,7 @@
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message,
             boolean statusReportRequested) {
@@ -496,6 +510,7 @@
      *         address, if applicable, and the encoded message.
      *         Returns null on encode error.
      */
+    @UnsupportedAppUsage
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message,
             boolean statusReportRequested, int validityPeriod) {
@@ -576,6 +591,7 @@
      * @param ret <code>SubmitPdu</code> containing the encoded SC
      *        address, if applicable, and the encoded message. Returns null on encode error.
      */
+    @UnsupportedAppUsage
     private static ByteArrayOutputStream getSubmitPduHead(
             String scAddress, String destinationAddress, byte mtiByte,
             boolean statusReportRequested, SubmitPdu ret) {
@@ -622,12 +638,16 @@
     }
 
     private static class PduParser {
+        @UnsupportedAppUsage
         byte mPdu[];
+        @UnsupportedAppUsage
         int mCur;
         SmsHeader mUserDataHeader;
         byte[] mUserData;
+        @UnsupportedAppUsage
         int mUserDataSeptetPadding;
 
+        @UnsupportedAppUsage
         PduParser(byte[] pdu) {
             mPdu = pdu;
             mCur = 0;
@@ -667,6 +687,7 @@
         /**
          * returns non-sign-extended byte value
          */
+        @UnsupportedAppUsage
         int getByte() {
             return mPdu[mCur++] & 0xff;
         }
@@ -808,6 +829,7 @@
          *
          * @return the user data payload, not including the headers
          */
+        @UnsupportedAppUsage
         byte[] getUserData() {
             return mUserData;
         }
@@ -864,6 +886,7 @@
          * @param byteCount the number of bytes in the user data payload
          * @return a String with the decoded characters
          */
+        @UnsupportedAppUsage
         String getUserDataUCS2(int byteCount) {
             String ret;
 
@@ -912,6 +935,7 @@
      * @param use7bitOnly ignore (but still count) illegal characters if true
      * @return TextEncodingDetails
      */
+    @UnsupportedAppUsage
     public static TextEncodingDetails calculateLength(CharSequence msgBody,
             boolean use7bitOnly) {
         CharSequence newMsgBody = null;
@@ -959,6 +983,7 @@
     }
 
     /** {@inheritDoc} */
+    @UnsupportedAppUsage
     @Override
     public boolean isMWIClearMessage() {
         if (mIsMwi && !mMwiSense) {
@@ -970,6 +995,7 @@
     }
 
     /** {@inheritDoc} */
+    @UnsupportedAppUsage
     @Override
     public boolean isMWISetMessage() {
         if (mIsMwi && mMwiSense) {
@@ -981,6 +1007,7 @@
     }
 
     /** {@inheritDoc} */
+    @UnsupportedAppUsage
     @Override
     public boolean isMwiDontStore() {
         if (mIsMwi && mMwiDontStore) {
@@ -1000,12 +1027,14 @@
     }
 
     /** {@inheritDoc} */
+    @UnsupportedAppUsage
     @Override
     public int getStatus() {
         return mStatus;
     }
 
     /** {@inheritDoc} */
+    @UnsupportedAppUsage
     @Override
     public boolean isStatusReportMessage() {
         return mIsStatusReportMessage;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 6b3df94..9c69e2d 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -24,6 +24,8 @@
 
 import com.android.internal.telephony.GsmAlphabet;
 
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
 import java.io.UnsupportedEncodingException;
 
 /**
@@ -46,6 +48,7 @@
      *
      * Stops on invalid BCD value, returning string so far
      */
+    @UnsupportedAppUsage
     public static String
     bcdToString(byte[] data, int offset, int length) {
         StringBuilder ret = new StringBuilder(length*2);
@@ -173,6 +176,7 @@
     /**
      * Decode cdma byte into String.
      */
+    @UnsupportedAppUsage
     public static String
     cdmaBcdToString(byte[] data, int offset, int length) {
         StringBuilder ret = new StringBuilder(length);
@@ -208,6 +212,7 @@
      * assume the digit is set to 0 but shall store the entire field
      * exactly as received"
      */
+    @UnsupportedAppUsage
     public static int
     gsmBcdByteToInt(byte b) {
         int ret = 0;
@@ -230,6 +235,7 @@
      * is in the least significant nibble and the most significant
      * is in the most significant nibble.
      */
+    @UnsupportedAppUsage
     public static int
     cdmaBcdByteToInt(byte b) {
         int ret = 0;
@@ -281,6 +287,7 @@
      *          contain a 16 bit number which defines the complete 16 bit
      *          base pointer to a "half page" in the UCS2 code space...
      */
+    @UnsupportedAppUsage
     public static String
     adnStringFieldToString(byte[] data, int offset, int length) {
         if (length == 0) {
@@ -372,6 +379,7 @@
         return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
     }
 
+    @UnsupportedAppUsage
     public static int
     hexCharToInt(char c) {
         if (c >= '0' && c <= '9') return (c - '0');
@@ -391,6 +399,7 @@
      *
      * @throws RuntimeException on invalid format
      */
+    @UnsupportedAppUsage
     public static byte[]
     hexStringToBytes(String s) {
         byte[] ret;
@@ -417,6 +426,7 @@
      *
      * @return hex string representation of bytes array
      */
+    @UnsupportedAppUsage
     public static String
     bytesToHexString(byte[] bytes) {
         if (bytes == null) return null;
@@ -444,6 +454,7 @@
      * "offset" points to "octet 3", the coding scheme byte
      * empty string returned on decode error
      */
+    @UnsupportedAppUsage
     public static String
     networkNameToString(byte[] data, int offset, int length) {
         String ret;
@@ -494,6 +505,7 @@
      * @param length The length of image body
      * @return The bitmap
      */
+    @UnsupportedAppUsage
     public static Bitmap parseToBnW(byte[] data, int length){
         int valueIndex = 0;
         int width = data[valueIndex++] & 0xFF;
@@ -536,6 +548,7 @@
      * @param transparency with or without transparency
      * @return The color bitmap
      */
+    @UnsupportedAppUsage
     public static Bitmap parseToRGB(byte[] data, int length,
             boolean transparency) {
         int valueIndex = 0;
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 232b5cb..c42201f 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -35,8 +35,8 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
-import android.net.NetworkStackClient;
-import android.net.NetworkStackClient.NetworkStackHealthListener;
+import android.net.ConnectivityModuleConnector;
+import android.net.ConnectivityModuleConnector.ConnectivityModuleHealthListener;
 import android.os.Handler;
 import android.os.test.TestLooper;
 import android.provider.DeviceConfig;
@@ -86,11 +86,11 @@
     private TestLooper mTestLooper;
     private Context mSpyContext;
     @Mock
-    private NetworkStackClient mMockNetworkStackClient;
+    private ConnectivityModuleConnector mConnectivityModuleConnector;
     @Mock
     private PackageManager mMockPackageManager;
     @Captor
-    private ArgumentCaptor<NetworkStackHealthListener> mNetworkStackCallbackCaptor;
+    private ArgumentCaptor<ConnectivityModuleHealthListener> mConnectivityModuleCallbackCaptor;
 
     @Before
     public void setUp() throws Exception {
@@ -736,7 +736,7 @@
         wd.startObservingHealth(observer, Collections.singletonList(APP_A), SHORT_DURATION);
 
         // Notify of NetworkStack failure
-        mNetworkStackCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
+        mConnectivityModuleCallbackCaptor.getValue().onNetworkStackFailure(APP_A);
 
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
@@ -782,18 +782,18 @@
         Handler handler = new Handler(mTestLooper.getLooper());
         PackageWatchdog watchdog =
                 new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
-                        mMockNetworkStackClient);
+                        mConnectivityModuleConnector);
         // Verify controller is not automatically started
         assertFalse(controller.mIsEnabled);
         if (withPackagesReady) {
             // Only capture the NetworkStack callback for the latest registered watchdog
-            reset(mMockNetworkStackClient);
+            reset(mConnectivityModuleConnector);
             watchdog.onPackagesReady();
             // Verify controller by default is started when packages are ready
             assertTrue(controller.mIsEnabled);
 
-            verify(mMockNetworkStackClient).registerHealthListener(
-                    mNetworkStackCallbackCaptor.capture());
+            verify(mConnectivityModuleConnector).registerHealthListener(
+                    mConnectivityModuleCallbackCaptor.capture());
         }
         return watchdog;
     }
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 2c0432a..720c1af 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -929,7 +929,6 @@
      */
     @Test
     public void testBadUpdateRollback() throws Exception {
-        BroadcastReceiver crashCountReceiver = null;
         Context context = InstrumentationRegistry.getContext();
         try {
             InstallUtils.adoptShellPermissionIdentity(
@@ -937,7 +936,7 @@
                     Manifest.permission.DELETE_PACKAGES,
                     Manifest.permission.MANAGE_ROLLBACKS,
                     Manifest.permission.TEST_MANAGE_ROLLBACKS,
-                    Manifest.permission.KILL_BACKGROUND_PROCESSES,
+                    Manifest.permission.FORCE_STOP_PACKAGES,
                     Manifest.permission.RESTART_PACKAGES);
             RollbackManager rm = RollbackUtils.getRollbackManager();
 
@@ -967,7 +966,7 @@
             RollbackBroadcastReceiver rollbackReceiver = new RollbackBroadcastReceiver();
 
             // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
-            crashCountReceiver = RollbackUtils.sendCrashBroadcast(context, TestApp.A, 5);
+            RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
 
             // Verify we received a broadcast for the rollback.
             rollbackReceiver.take();
@@ -981,9 +980,6 @@
             assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
         } finally {
             InstallUtils.dropShellPermissionIdentity();
-            if (crashCountReceiver != null) {
-                context.unregisterReceiver(crashCountReceiver);
-            }
         }
     }
 
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index db81831..9e6ac8e 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -25,7 +25,6 @@
 
 import android.Manifest;
 import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -77,7 +76,7 @@
                 Manifest.permission.INSTALL_PACKAGES,
                 Manifest.permission.DELETE_PACKAGES,
                 Manifest.permission.TEST_MANAGE_ROLLBACKS,
-                Manifest.permission.KILL_BACKGROUND_PROCESSES);
+                Manifest.permission.FORCE_STOP_PACKAGES);
     }
 
     /**
@@ -135,17 +134,8 @@
      */
     @Test
     public void testBadApkOnlyTriggerRollback() throws Exception {
-        BroadcastReceiver crashCountReceiver = null;
-        Context context = InstrumentationRegistry.getContext();
-
-        try {
-            // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
-            crashCountReceiver = RollbackUtils.sendCrashBroadcast(context, TestApp.A, 5);
-        } finally {
-            if (crashCountReceiver != null) {
-                context.unregisterReceiver(crashCountReceiver);
-            }
-        }
+        // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
+        RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
 
         // We expect the device to be rebooted automatically. Wait for that to
         // happen. At that point, the host test driver will wait for the
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8c522f4..c030c3e 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -469,6 +469,8 @@
         if (emulateInterfaceStatusChanged) {
             assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
             verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+            verify(mWifiManager).updateInterfaceIpState(
+                    TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         }
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
@@ -534,6 +536,8 @@
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+        verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
@@ -554,6 +558,8 @@
                 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mWifiManager, times(3)).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
@@ -739,6 +745,8 @@
 
         assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+        verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
     }
@@ -768,6 +776,8 @@
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+        verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
@@ -805,6 +815,8 @@
                 .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
+        verify(mWifiManager, times(3)).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
         // Asking for the last error after the per-interface state machine
@@ -842,6 +854,8 @@
         verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
         verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+        verify(mWifiManager).updateInterfaceIpState(
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
         assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 2b2e8a7..5217e26 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -406,6 +406,13 @@
     }
 
     @Test
+    public void verifyPermissionWhenProvisioningNotStarted() {
+        assertTrue(mEnMgr.isCellularUpstreamPermitted());
+        setupForRequiredProvisioning();
+        assertFalse(mEnMgr.isCellularUpstreamPermitted());
+    }
+
+    @Test
     public void testRunTetherProvisioning() {
         setupForRequiredProvisioning();
         // 1. start ui provisioning, upstream is mobile
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index bbf71e7..99a686b 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1084,7 +1084,8 @@
 
       case OutputFormat::kProto: {
         pb::ResourceTable pb_table;
-        SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics());
+        SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics(),
+                           options_.proto_table_flattener_options);
         return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
                                       ArchiveEntry::kCompress, writer);
       } break;
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 324807c..56bff8f 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -24,6 +24,7 @@
 #include "Resource.h"
 #include "split/TableSplitter.h"
 #include "format/binary/TableFlattener.h"
+#include "format/proto/ProtoSerialize.h"
 #include "link/ManifestFixer.h"
 #include "trace/TraceBuffer.h"
 
@@ -81,6 +82,7 @@
 
   // Flattening options.
   TableFlattenerOptions table_flattener_options;
+  SerializeTableOptions proto_table_flattener_options;
   bool keep_raw_values = false;
 
   // Split APK options.
@@ -245,9 +247,9 @@
             "<add-resource> tags.",
         &options_.auto_add_overlay);
     AddOptionalSwitch("--override-styles-instead-of-overlaying",
-                      "Causes styles defined in -R resources to replace previous definitions\n"
-                      "instead of merging into them\n",
-                      &options_.override_styles_instead_of_overlaying);
+        "Causes styles defined in -R resources to replace previous definitions\n"
+            "instead of merging into them\n",
+        &options_.override_styles_instead_of_overlaying);
     AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
         &options_.manifest_fixer_options.rename_manifest_package);
     AddOptionalFlag("--rename-instrumentation-target-package",
@@ -283,13 +285,18 @@
     AddOptionalSwitch("--strict-visibility",
         "Do not allow overlays with different visibility levels.",
         &options_.strict_visibility);
-    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
-    AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
-                    &trace_folder_);
+    AddOptionalSwitch("--exclude-sources",
+        "Do not serialize source file information when generating resources in\n"
+            "Protobuf format.",
+        &options_.proto_table_flattener_options.exclude_sources);
+    AddOptionalFlag("--trace-folder",
+        "Generate systrace json trace fragment to specified folder.",
+        &trace_folder_);
     AddOptionalSwitch("--merge-only",
-          "Only merge the resources, without verifying resource references. This flag\n"
-          "should only be used together with the --static-lib flag.",
-          &options_.merge_only);
+        "Only merge the resources, without verifying resource references. This flag\n"
+            "should only be used together with the --static-lib flag.",
+        &options_.merge_only);
+    AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
   }
 
   int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index aa6547e..e4b3fce 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -290,8 +290,10 @@
     pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
     pb_overlayable->set_name(overlayable_item.overlayable->name);
     pb_overlayable->set_actor(overlayable_item.overlayable->actor);
-    SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
-                        pb_overlayable->mutable_source());
+    if (source_pool != nullptr) {
+      SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+                          pb_overlayable->mutable_source());
+    }
   }
 
   pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
@@ -319,14 +321,17 @@
     pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
   }
 
-  SerializeSourceToPb(overlayable_item.source, source_pool,
-                      pb_overlayable_item->mutable_source());
+  if (source_pool != nullptr) {
+    SerializeSourceToPb(overlayable_item.source, source_pool,
+                        pb_overlayable_item->mutable_source());
+  }
   pb_overlayable_item->set_comment(overlayable_item.comment);
 }
 
 void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
-                        IDiagnostics* diag) {
-  StringPool source_pool;
+                        IDiagnostics* diag, SerializeTableOptions options) {
+  auto source_pool = (options.exclude_sources) ? nullptr : util::make_unique<StringPool>();
+
   pb::ToolFingerprint* pb_fingerprint = out_table->add_tool_fingerprint();
   pb_fingerprint->set_tool(util::GetToolName());
   pb_fingerprint->set_version(util::GetToolFingerprint());
@@ -356,32 +361,40 @@
         // Write the Visibility struct.
         pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
         pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
-        SerializeSourceToPb(entry->visibility.source, &source_pool,
-                            pb_visibility->mutable_source());
+        if (source_pool != nullptr) {
+          SerializeSourceToPb(entry->visibility.source, source_pool.get(),
+                              pb_visibility->mutable_source());
+        }
         pb_visibility->set_comment(entry->visibility.comment);
 
         if (entry->allow_new) {
           pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
-          SerializeSourceToPb(entry->allow_new.value().source, &source_pool,
-                              pb_allow_new->mutable_source());
+          if (source_pool != nullptr) {
+            SerializeSourceToPb(entry->allow_new.value().source, source_pool.get(),
+                                pb_allow_new->mutable_source());
+          }
           pb_allow_new->set_comment(entry->allow_new.value().comment);
         }
 
         if (entry->overlayable_item) {
-          SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
-                                       pb_entry, out_table);
+          SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables,
+                                       source_pool.get(), pb_entry, out_table);
         }
 
         for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
           pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
           SerializeConfig(config_value->config, pb_config_value->mutable_config());
           pb_config_value->mutable_config()->set_product(config_value->product);
-          SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(), &source_pool);
+          SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
+                             source_pool.get());
         }
       }
     }
   }
-  SerializeStringPoolToPb(source_pool, out_table->mutable_source_pool(), diag);
+
+  if (source_pool != nullptr) {
+    SerializeStringPoolToPb(*source_pool, out_table->mutable_source_pool(), diag);
+  }
 }
 
 static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
index 33ffd18..7a3ea99 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.h
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -35,6 +35,11 @@
   bool remove_empty_text_nodes = false;
 };
 
+struct SerializeTableOptions {
+    /** Prevent serializing the source pool and source protos.  */
+    bool exclude_sources = false;
+};
+
 // Serializes a Value to its protobuf representation. An optional StringPool will hold the
 // source path string.
 void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool = nullptr);
@@ -59,7 +64,8 @@
 void SerializeConfig(const android::ConfigDescription& config, pb::Configuration* out_pb_config);
 
 // Serializes a ResourceTable into its protobuf representation.
-void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table, IDiagnostics* diag);
+void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
+                        IDiagnostics* diag, SerializeTableOptions options = {});
 
 // Serializes a ResourceFile into its protobuf representation.
 void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file);
diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp
index c81d410..58a7d34 100644
--- a/tools/processors/staledataclass/Android.bp
+++ b/tools/processors/staledataclass/Android.bp
@@ -13,6 +13,8 @@
     static_libs: [
         "codegen-version-info",
     ],
+    // The --add-modules/exports flags below don't work for kotlinc yet, so pin this module to Java language level 8 (see b/139342589):
+    java_version: "1.8",
     openjdk9: {
         javacflags: [
             "--add-modules=jdk.compiler",
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 20d772c..ca65736 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -50,6 +50,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.WorkSource;
 import android.util.Log;
 import android.util.Pair;
@@ -1149,18 +1150,40 @@
      * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
      * the standard {@link android.content.Context#WIFI_SERVICE Context.WIFI_SERVICE}.
      * @param context the application context
-     * @param service the Binder interface
      * @hide - hide this because it takes in a parameter of type IWifiManager, which
      * is a system private class.
      */
-    public WifiManager(Context context, IWifiManager service, Looper looper) {
+    public WifiManager(Context context, Looper looper) {
         mContext = context;
-        mService = service;
         mLooper = looper;
         mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+    }
+
+    /**
+     * This is used only for unit testing.
+     * @hide
+     */
+    @VisibleForTesting
+    public WifiManager(Context context, IWifiManager service, Looper looper) {
+        this(context, looper);
+        mService = service;
         updateVerboseLoggingEnabledFromService();
     }
 
+    private IWifiManager getIWifiManager() throws RemoteException {
+        if (mService == null) {
+            synchronized (this) {
+                mService = IWifiManager.Stub.asInterface(
+                        ServiceManager.getService(Context.WIFI_SERVICE));
+                if (mService == null) {
+                    throw new RemoteException("Wifi Service not running");
+                }
+                updateVerboseLoggingEnabledFromService();
+            }
+        }
+        return mService;
+    }
+
     /**
      * Return a list of all the networks configured for the current foreground
      * user.
@@ -1201,7 +1224,7 @@
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
-                    mService.getConfiguredNetworks(mContext.getOpPackageName());
+                    getIWifiManager().getConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1217,7 +1240,7 @@
     public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
         try {
             ParceledListSlice<WifiConfiguration> parceledList =
-                    mService.getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
+                    getIWifiManager().getPrivilegedConfiguredNetworks(mContext.getOpPackageName());
             if (parceledList == null) {
                 return Collections.emptyList();
             }
@@ -1249,13 +1272,13 @@
         List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>();
         try {
             Map<String, Map<Integer, List<ScanResult>>> results =
-                    mService.getAllMatchingFqdnsForScanResults(
+                    getIWifiManager().getAllMatchingFqdnsForScanResults(
                             scanResults);
             if (results.isEmpty()) {
                 return configs;
             }
             List<WifiConfiguration> wifiConfigurations =
-                    mService.getWifiConfigsForPasspointProfiles(
+                    getIWifiManager().getWifiConfigsForPasspointProfiles(
                             new ArrayList<>(results.keySet()));
             for (WifiConfiguration configuration : wifiConfigurations) {
                 Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get(
@@ -1293,7 +1316,7 @@
             return new HashMap<>();
         }
         try {
-            return mService.getMatchingOsuProviders(scanResults);
+            return getIWifiManager().getMatchingOsuProviders(scanResults);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1320,7 +1343,7 @@
     public Map<OsuProvider, PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(
             @NonNull Set<OsuProvider> osuProviders) {
         try {
-            return mService.getMatchingPasspointConfigsForOsuProviders(
+            return getIWifiManager().getMatchingPasspointConfigsForOsuProviders(
                     new ArrayList<>(osuProviders));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1405,7 +1428,7 @@
      */
     private int addOrUpdateNetwork(WifiConfiguration config) {
         try {
-            return mService.addOrUpdateNetwork(config, mContext.getOpPackageName());
+            return getIWifiManager().addOrUpdateNetwork(config, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1631,7 +1654,7 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            mService.registerNetworkRequestMatchCallback(
+            getIWifiManager().registerNetworkRequestMatchCallback(
                     binder, new NetworkRequestMatchCallbackProxy(looper, callback),
                     callback.hashCode());
         } catch (RemoteException e) {
@@ -1657,7 +1680,7 @@
         Log.v(TAG, "unregisterNetworkRequestMatchCallback: callback=" + callback);
 
         try {
-            mService.unregisterNetworkRequestMatchCallback(callback.hashCode());
+            getIWifiManager().unregisterNetworkRequestMatchCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1691,7 +1714,8 @@
     public @NetworkSuggestionsStatusCode int addNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return mService.addNetworkSuggestions(networkSuggestions, mContext.getOpPackageName());
+            return getIWifiManager().addNetworkSuggestions(
+                    networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1714,7 +1738,7 @@
     public @NetworkSuggestionsStatusCode int removeNetworkSuggestions(
             @NonNull List<WifiNetworkSuggestion> networkSuggestions) {
         try {
-            return mService.removeNetworkSuggestions(
+            return getIWifiManager().removeNetworkSuggestions(
                     networkSuggestions, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -1760,7 +1784,8 @@
      */
     public void addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
         try {
-            if (!mService.addOrUpdatePasspointConfiguration(config, mContext.getOpPackageName())) {
+            if (!getIWifiManager().addOrUpdatePasspointConfiguration(
+                    config, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
         } catch (RemoteException e) {
@@ -1783,7 +1808,8 @@
     })
     public void removePasspointConfiguration(String fqdn) {
         try {
-            if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) {
+            if (!getIWifiManager().removePasspointConfiguration(
+                    fqdn, mContext.getOpPackageName())) {
                 throw new IllegalArgumentException();
             }
         } catch (RemoteException e) {
@@ -1806,7 +1832,7 @@
     })
     public List<PasspointConfiguration> getPasspointConfigurations() {
         try {
-            return mService.getPasspointConfigurations(mContext.getOpPackageName());
+            return getIWifiManager().getPasspointConfigurations(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1826,7 +1852,7 @@
      */
     public void queryPasspointIcon(long bssid, String fileName) {
         try {
-            mService.queryPasspointIcon(bssid, fileName);
+            getIWifiManager().queryPasspointIcon(bssid, fileName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1840,7 +1866,7 @@
      */
     public int matchProviderWithCurrentNetwork(String fqdn) {
         try {
-            return mService.matchProviderWithCurrentNetwork(fqdn);
+            return getIWifiManager().matchProviderWithCurrentNetwork(fqdn);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1854,7 +1880,7 @@
      */
     public void deauthenticateNetwork(long holdoff, boolean ess) {
         try {
-            mService.deauthenticateNetwork(holdoff, ess);
+            getIWifiManager().deauthenticateNetwork(holdoff, ess);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1884,7 +1910,7 @@
     @Deprecated
     public boolean removeNetwork(int netId) {
         try {
-            return mService.removeNetwork(netId, mContext.getOpPackageName());
+            return getIWifiManager().removeNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1939,7 +1965,8 @@
 
         boolean success;
         try {
-            success = mService.enableNetwork(netId, attemptConnect, mContext.getOpPackageName());
+            success = getIWifiManager().enableNetwork(
+                    netId, attemptConnect, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1975,7 +2002,7 @@
     @Deprecated
     public boolean disableNetwork(int netId) {
         try {
-            return mService.disableNetwork(netId, mContext.getOpPackageName());
+            return getIWifiManager().disableNetwork(netId, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1998,7 +2025,7 @@
     @Deprecated
     public boolean disconnect() {
         try {
-            return mService.disconnect(mContext.getOpPackageName());
+            return getIWifiManager().disconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2022,7 +2049,7 @@
     @Deprecated
     public boolean reconnect() {
         try {
-            return mService.reconnect(mContext.getOpPackageName());
+            return getIWifiManager().reconnect(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2046,7 +2073,7 @@
     @Deprecated
     public boolean reassociate() {
         try {
-            return mService.reassociate(mContext.getOpPackageName());
+            return getIWifiManager().reassociate(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2132,7 +2159,7 @@
 
     private long getSupportedFeatures() {
         try {
-            return mService.getSupportedFeatures();
+            return getIWifiManager().getSupportedFeatures();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2261,10 +2288,9 @@
      * @hide
      */
     public WifiActivityEnergyInfo getControllerActivityEnergyInfo() {
-        if (mService == null) return null;
         try {
             synchronized(this) {
-                return mService.reportActivityInfo();
+                return getIWifiManager().reportActivityInfo();
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -2304,7 +2330,7 @@
     public boolean startScan(WorkSource workSource) {
         try {
             String packageName = mContext.getOpPackageName();
-            return mService.startScan(packageName);
+            return getIWifiManager().startScan(packageName);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2333,7 +2359,7 @@
      */
     public WifiInfo getConnectionInfo() {
         try {
-            return mService.getConnectionInfo(mContext.getOpPackageName());
+            return getIWifiManager().getConnectionInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2347,7 +2373,7 @@
      */
     public List<ScanResult> getScanResults() {
         try {
-            return mService.getScanResults(mContext.getOpPackageName());
+            return getIWifiManager().getScanResults(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2366,7 +2392,7 @@
     @Deprecated
     public boolean isScanAlwaysAvailable() {
         try {
-            return mService.isScanAlwaysAvailable();
+            return getIWifiManager().isScanAlwaysAvailable();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2397,7 +2423,7 @@
      */
     public void setCountryCode(@NonNull String country) {
         try {
-            mService.setCountryCode(country);
+            getIWifiManager().setCountryCode(country);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2411,12 +2437,12 @@
     */
     @UnsupportedAppUsage
     public String getCountryCode() {
-       try {
-           String country = mService.getCountryCode();
-           return country;
-       } catch (RemoteException e) {
-           throw e.rethrowFromSystemServer();
-       }
+        try {
+            String country = getIWifiManager().getCountryCode();
+            return country;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -2427,7 +2453,7 @@
     @UnsupportedAppUsage
     public boolean isDualBandSupported() {
         try {
-            return mService.isDualBandSupported();
+            return getIWifiManager().isDualBandSupported();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2440,7 +2466,7 @@
      */
     public boolean isDualModeSupported() {
         try {
-            return mService.needs5GHzToAnyApBandConversion();
+            return getIWifiManager().needs5GHzToAnyApBandConversion();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2453,7 +2479,7 @@
      */
     public DhcpInfo getDhcpInfo() {
         try {
-            return mService.getDhcpInfo();
+            return getIWifiManager().getDhcpInfo();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2480,7 +2506,7 @@
     @Deprecated
     public boolean setWifiEnabled(boolean enabled) {
         try {
-            return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
+            return getIWifiManager().setWifiEnabled(mContext.getOpPackageName(), enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2495,7 +2521,7 @@
      */
     public int getWifiState() {
         try {
-            return mService.getWifiEnabledState();
+            return getIWifiManager().getWifiEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2569,7 +2595,7 @@
      */
     public void updateInterfaceIpState(String ifaceName, int mode) {
         try {
-            mService.updateInterfaceIpState(ifaceName, mode);
+            getIWifiManager().updateInterfaceIpState(ifaceName, mode);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2587,7 +2613,7 @@
      */
     public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) {
         try {
-            return mService.startSoftAp(wifiConfig);
+            return getIWifiManager().startSoftAp(wifiConfig);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2602,7 +2628,7 @@
      */
     public boolean stopSoftAp() {
         try {
-            return mService.stopSoftAp();
+            return getIWifiManager().stopSoftAp();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2671,7 +2697,7 @@
                     new LocalOnlyHotspotCallbackProxy(this, looper, callback);
             try {
                 String packageName = mContext.getOpPackageName();
-                int returnCode = mService.startLocalOnlyHotspot(
+                int returnCode = getIWifiManager().startLocalOnlyHotspot(
                         proxy.getMessenger(), new Binder(), packageName);
                 if (returnCode != LocalOnlyHotspotCallback.REQUEST_REGISTERED) {
                     // Send message to the proxy to make sure we call back on the correct thread
@@ -2723,7 +2749,7 @@
             }
             mLOHSCallbackProxy = null;
             try {
-                mService.stopLocalOnlyHotspot();
+                getIWifiManager().stopLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2753,7 +2779,7 @@
             Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
             mLOHSObserverProxy = new LocalOnlyHotspotObserverProxy(this, looper, observer);
             try {
-                mService.startWatchLocalOnlyHotspot(
+                getIWifiManager().startWatchLocalOnlyHotspot(
                         mLOHSObserverProxy.getMessenger(), new Binder());
                 mLOHSObserverProxy.registered();
             } catch (RemoteException e) {
@@ -2777,7 +2803,7 @@
             }
             mLOHSObserverProxy = null;
             try {
-                mService.stopWatchLocalOnlyHotspot();
+                getIWifiManager().stopWatchLocalOnlyHotspot();
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -2797,7 +2823,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public int getWifiApState() {
         try {
-            return mService.getWifiApEnabledState();
+            return getIWifiManager().getWifiApEnabledState();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2826,7 +2852,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE)
     public WifiConfiguration getWifiApConfiguration() {
         try {
-            return mService.getWifiApConfiguration();
+            return getIWifiManager().getWifiApConfiguration();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2843,7 +2869,8 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
+            return getIWifiManager().setWifiApConfiguration(
+                    wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2858,7 +2885,7 @@
     public void notifyUserOfApBandConversion() {
         Log.d(TAG, "apBand was converted, notify the user");
         try {
-            mService.notifyUserOfApBandConversion(mContext.getOpPackageName());
+            getIWifiManager().notifyUserOfApBandConversion(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2886,7 +2913,7 @@
      */
     public void setTdlsEnabled(InetAddress remoteIPAddress, boolean enable) {
         try {
-            mService.enableTdls(remoteIPAddress.getHostAddress(), enable);
+            getIWifiManager().enableTdls(remoteIPAddress.getHostAddress(), enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2900,7 +2927,7 @@
      */
     public void setTdlsEnabledWithMacAddress(String remoteMacAddress, boolean enable) {
         try {
-            mService.enableTdlsWithMacAddress(remoteMacAddress, enable);
+            getIWifiManager().enableTdlsWithMacAddress(remoteMacAddress, enable);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3189,8 +3216,8 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            mService.registerSoftApCallback(binder, new SoftApCallbackProxy(looper, callback),
-                    callback.hashCode());
+            getIWifiManager().registerSoftApCallback(
+                    binder, new SoftApCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3210,7 +3237,7 @@
         Log.v(TAG, "unregisterSoftApCallback: callback=" + callback);
 
         try {
-            mService.unregisterSoftApCallback(callback.hashCode());
+            getIWifiManager().unregisterSoftApCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3780,7 +3807,7 @@
     public void disableEphemeralNetwork(String SSID) {
         if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
         try {
-            mService.disableEphemeralNetwork(SSID, mContext.getOpPackageName());
+            getIWifiManager().disableEphemeralNetwork(SSID, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3824,7 +3851,7 @@
     @UnsupportedAppUsage
     private Messenger getWifiServiceMessenger() {
         try {
-            return mService.getWifiServiceMessenger(mContext.getOpPackageName());
+            return getIWifiManager().getWifiServiceMessenger(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3884,10 +3911,10 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        mService.acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
+                        getIWifiManager().acquireWifiLock(mBinder, mLockType, mTag, mWorkSource);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                mService.releaseWifiLock(mBinder);
+                                getIWifiManager().releaseWifiLock(mBinder);
                                 throw new UnsupportedOperationException(
                                             "Exceeded maximum number of wifi locks");
                             }
@@ -3917,7 +3944,7 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        mService.releaseWifiLock(mBinder);
+                        getIWifiManager().releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -3980,7 +4007,7 @@
                 }
                 if (changed && mHeld) {
                     try {
-                        mService.updateWifiLockWorkSource(mBinder, mWorkSource);
+                        getIWifiManager().updateWifiLockWorkSource(mBinder, mWorkSource);
                     } catch (RemoteException e) {
                         throw e.rethrowFromSystemServer();
                     }
@@ -4008,7 +4035,7 @@
             synchronized (mBinder) {
                 if (mHeld) {
                     try {
-                        mService.releaseWifiLock(mBinder);
+                        getIWifiManager().releaseWifiLock(mBinder);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4121,10 +4148,10 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (++mRefCount == 1) : (!mHeld)) {
                     try {
-                        mService.acquireMulticastLock(mBinder, mTag);
+                        getIWifiManager().acquireMulticastLock(mBinder, mTag);
                         synchronized (WifiManager.this) {
                             if (mActiveLockCount >= MAX_ACTIVE_LOCKS) {
-                                mService.releaseMulticastLock(mTag);
+                                getIWifiManager().releaseMulticastLock(mTag);
                                 throw new UnsupportedOperationException(
                                         "Exceeded maximum number of wifi locks");
                             }
@@ -4166,7 +4193,7 @@
             synchronized (mBinder) {
                 if (mRefCounted ? (--mRefCount == 0) : (mHeld)) {
                     try {
-                        mService.releaseMulticastLock(mTag);
+                        getIWifiManager().releaseMulticastLock(mTag);
                         synchronized (WifiManager.this) {
                             mActiveLockCount--;
                         }
@@ -4243,7 +4270,7 @@
      */
     public boolean isMulticastEnabled() {
         try {
-            return mService.isMulticastEnabled();
+            return getIWifiManager().isMulticastEnabled();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4256,7 +4283,7 @@
     @UnsupportedAppUsage
     public boolean initializeMulticastFiltering() {
         try {
-            mService.initializeMulticastFiltering();
+            getIWifiManager().initializeMulticastFiltering();
             return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4281,7 +4308,7 @@
     @UnsupportedAppUsage
     public void enableVerboseLogging (int verbose) {
         try {
-            mService.enableVerboseLogging(verbose);
+            getIWifiManager().enableVerboseLogging(verbose);
         } catch (Exception e) {
             //ignore any failure here
             Log.e(TAG, "enableVerboseLogging " + e.toString());
@@ -4296,7 +4323,7 @@
     @UnsupportedAppUsage
     public int getVerboseLoggingLevel() {
         try {
-            return mService.getVerboseLoggingLevel();
+            return getIWifiManager().getVerboseLoggingLevel();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4309,7 +4336,7 @@
      */
     public void factoryReset() {
         try {
-            mService.factoryReset(mContext.getOpPackageName());
+            getIWifiManager().factoryReset(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4323,7 +4350,7 @@
     @UnsupportedAppUsage
     public Network getCurrentNetwork() {
         try {
-            return mService.getCurrentNetwork();
+            return getIWifiManager().getCurrentNetwork();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4355,7 +4382,7 @@
      */
     public void enableWifiConnectivityManager(boolean enabled) {
         try {
-            mService.enableWifiConnectivityManager(enabled);
+            getIWifiManager().enableWifiConnectivityManager(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4367,7 +4394,7 @@
      */
     public byte[] retrieveBackupData() {
         try {
-            return mService.retrieveBackupData();
+            return getIWifiManager().retrieveBackupData();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4379,7 +4406,7 @@
      */
     public void restoreBackupData(byte[] data) {
         try {
-            mService.restoreBackupData(data);
+            getIWifiManager().restoreBackupData(data);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4395,7 +4422,7 @@
     @Deprecated
     public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) {
         try {
-            mService.restoreSupplicantBackupData(supplicantData, ipConfigData);
+            getIWifiManager().restoreSupplicantBackupData(supplicantData, ipConfigData);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4424,7 +4451,7 @@
             throw new IllegalArgumentException("callback must not be null");
         }
         try {
-            mService.startSubscriptionProvisioning(provider,
+            getIWifiManager().startSubscriptionProvisioning(provider,
                     new ProvisioningCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4538,7 +4565,7 @@
         Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
         Binder binder = new Binder();
         try {
-            mService.registerTrafficStateCallback(
+            getIWifiManager().registerTrafficStateCallback(
                     binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4558,7 +4585,7 @@
         Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback);
 
         try {
-            mService.unregisterTrafficStateCallback(callback.hashCode());
+            getIWifiManager().unregisterTrafficStateCallback(callback.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4614,7 +4641,7 @@
     @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
     public String[] getFactoryMacAddresses() {
         try {
-            return mService.getFactoryMacAddresses();
+            return getIWifiManager().getFactoryMacAddresses();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4683,7 +4710,7 @@
     @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE)
     public void setDeviceMobilityState(@DeviceMobilityState int state) {
         try {
-            mService.setDeviceMobilityState(state);
+            getIWifiManager().setDeviceMobilityState(state);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4738,8 +4765,9 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            mService.startDppAsConfiguratorInitiator(binder, enrolleeUri, selectedNetworkId,
-                    enrolleeNetworkRole, new EasyConnectCallbackProxy(executor, callback));
+            getIWifiManager().startDppAsConfiguratorInitiator(
+                    binder, enrolleeUri, selectedNetworkId, enrolleeNetworkRole,
+                    new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4764,7 +4792,7 @@
             @NonNull EasyConnectStatusCallback callback) {
         Binder binder = new Binder();
         try {
-            mService.startDppAsEnrolleeInitiator(binder, configuratorUri,
+            getIWifiManager().startDppAsEnrolleeInitiator(binder, configuratorUri,
                     new EasyConnectCallbackProxy(executor, callback));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -4786,7 +4814,7 @@
     public void stopEasyConnectSession() {
         try {
             /* Request lower layers to stop/abort and clear resources */
-            mService.stopDppSession();
+            getIWifiManager().stopDppSession();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4885,7 +4913,7 @@
             Log.v(TAG, "addOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            mService.addOnWifiUsabilityStatsListener(new Binder(),
+            getIWifiManager().addOnWifiUsabilityStatsListener(new Binder(),
                     new IOnWifiUsabilityStatsListener.Stub() {
                         @Override
                         public void onWifiUsabilityStats(int seqNum, boolean isSameBssidAndFreq,
@@ -4922,7 +4950,7 @@
             Log.v(TAG, "removeOnWifiUsabilityStatsListener: listener=" + listener);
         }
         try {
-            mService.removeOnWifiUsabilityStatsListener(listener.hashCode());
+            getIWifiManager().removeOnWifiUsabilityStatsListener(listener.hashCode());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -4945,7 +4973,7 @@
     @RequiresPermission(android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE)
     public void updateWifiUsabilityScore(int seqNum, int score, int predictionHorizonSec) {
         try {
-            mService.updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
+            getIWifiManager().updateWifiUsabilityScore(seqNum, score, predictionHorizonSec);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }