Merge "Remove old puller code from uid sandboxing"
diff --git a/apex/Android.bp b/apex/Android.bp
index c1b572a..6d4dc85 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -98,10 +98,8 @@
 
     annotations_enabled: true,
 
-    // The stub libraries must be visible to frameworks/base so they can be combined
-    // into API specific libraries.
     stubs_library_visibility: [
-        "//frameworks/base", // Framework
+        "//visibility:public",
     ],
 
     // Set the visibility of the modules creating the stubs source.
diff --git a/apex/media/framework/java/android/media/MediaSession2.java b/apex/media/framework/java/android/media/MediaSession2.java
index 081e76a..6560afe 100644
--- a/apex/media/framework/java/android/media/MediaSession2.java
+++ b/apex/media/framework/java/android/media/MediaSession2.java
@@ -404,7 +404,7 @@
                 mCallback.onPostConnect(MediaSession2.this, controllerInfo);
                 connected = true;
             } finally {
-                if (!connected) {
+                if (!connected || isClosed()) {
                     if (DEBUG) {
                         Log.d(TAG, "Rejecting connection or notifying that session is closed"
                                 + ", controllerInfo=" + controllerInfo);
diff --git a/api/system-current.txt b/api/system-current.txt
index e931fe4..5aec633 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -2525,6 +2525,7 @@
 
   public final class HdmiControlManager {
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
     method @Nullable public android.hardware.hdmi.HdmiClient getClient(int);
     method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
     method public int getPhysicalAddress();
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 73d9c22..63ba4aa 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -73,6 +73,12 @@
 
 ArrayReturn: android.app.UiAutomation#executeShellCommandRw(String):
     
+ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[]) parameter #3:
+    Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#KeyphraseSoundModel(java.util.UUID, java.util.UUID, byte[], android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int) parameter #3:
+    Method parameter should be Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
+ArrayReturn: android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel#getKeyphrases():
+    Method should return Collection<Keyphrase> (or subclass) instead of raw array; was `android.hardware.soundtrigger.SoundTrigger.Keyphrase[]`
 ArrayReturn: android.location.GnssMeasurementsEvent#GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]) parameter #1:
     
 ArrayReturn: android.media.AudioRecordingConfiguration#AudioRecordingConfiguration(int, int, int, android.media.AudioFormat, android.media.AudioFormat, int, String, int, boolean, int, android.media.audiofx.AudioEffect.Descriptor[], android.media.audiofx.AudioEffect.Descriptor[]) parameter #10:
@@ -116,7 +122,7 @@
 ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0:
     
 ArrayReturn: android.view.Display#getSupportedWideColorGamut():
-    Method should return Collection<ColorSpace> (or subclass) instead of raw array; was `android.graphics.ColorSpace[]`
+    
 ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
     
 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
@@ -211,6 +217,36 @@
     
 
 
+BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#allowPrivilegedPlaybackCapture(boolean):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.allowPrivilegedPlaybackCapture(boolean)
+BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeMixRule(int, Object):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeMixRule(int,Object)
+BuilderSetStyle: android.media.audiopolicy.AudioMixingRule.Builder#excludeRule(android.media.AudioAttributes, int):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.media.audiopolicy.AudioMixingRule.Builder.excludeRule(android.media.AudioAttributes,int)
+BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeCapability(int):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeCapability(int)
+BuilderSetStyle: android.net.NetworkCapabilities.Builder#removeTransportType(int):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.NetworkCapabilities.Builder.removeTransportType(int)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateDnsslLifetime(long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateDnsslLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixPreferredLifetime(long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixPreferredLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updatePrefixValidLifetime(long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updatePrefixValidLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRdnssLifetime(long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRdnssLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouteInfoLifetime(long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouteInfoLifetime(long)
+BuilderSetStyle: android.net.metrics.RaEvent.Builder#updateRouterLifetime(long):
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.metrics.RaEvent.Builder.updateRouterLifetime(long)
+BuilderSetStyle: android.os.StrictMode.ThreadPolicy.Builder#detectExplicitGc():
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.ThreadPolicy.Builder.detectExplicitGc()
+BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse():
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.detectIncorrectContextUse()
+BuilderSetStyle: android.os.StrictMode.VmPolicy.Builder#permitIncorrectContextUse():
+    Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.os.StrictMode.VmPolicy.Builder.permitIncorrectContextUse()
+
+
 CallbackInterface: android.app.prediction.AppPredictor.Callback:
     
 CallbackInterface: android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback:
@@ -345,8 +381,12 @@
     
 ExecutorRegistration: android.os.RemoteCallback#RemoteCallback(android.os.RemoteCallback.OnResultListener, android.os.Handler):
     
+ExecutorRegistration: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
+    Registration methods should have overload that accepts delivery Executor: `countPermissionApps`
 ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
     
+ExecutorRegistration: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
+    Registration methods should have overload that accepts delivery Executor: `setCallback`
 ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener):
     
 ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener):
@@ -365,6 +405,8 @@
     
 ExecutorRegistration: android.telephony.mbms.vendor.MbmsStreamingServiceBase#startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback):
     
+ExecutorRegistration: android.window.WindowOrganizer#applySyncTransaction(android.window.WindowContainerTransaction, android.window.WindowContainerTransactionCallback):
+    Registration methods should have overload that accepts delivery Executor: `applySyncTransaction`
 
 
 ForbiddenSuperClass: android.app.AppDetailsActivity:
@@ -385,6 +427,12 @@
     
 
 
+GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByCategory():
+    Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByCategory()
+GetterOnBuilder: android.hardware.display.BrightnessConfiguration.Builder#getMaxCorrectionsByPackageName():
+    Getter should be on the built object, not the builder: method android.hardware.display.BrightnessConfiguration.Builder.getMaxCorrectionsByPackageName()
+
+
 GetterSetterNames: android.app.NotificationChannel#isBlockableSystem():
     
 GetterSetterNames: android.app.NotificationChannel#isImportanceLockedByCriticalDeviceFunction():
@@ -461,6 +509,8 @@
     
 IntentBuilderName: android.app.backup.BackupManager#getDataManagementIntent(String):
     
+IntentBuilderName: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale):
+    Methods creating an Intent should be named `create<Foo>Intent()`, was `getManageKeyphraseIntent`
 
 
 IntentName: android.provider.Settings.Secure#VOICE_INTERACTION_SERVICE:
@@ -509,6 +559,8 @@
     
 ListenerLast: android.location.LocationManager#requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper) parameter #2:
     
+ListenerLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler) parameter #3:
+    Listeners should always be at end of argument list (method `countPermissionApps`)
 ListenerLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler) parameter #2:
     
 ListenerLast: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int) parameter #1:
@@ -541,6 +593,142 @@
     
 
 
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setAttributionTag(String):
+    android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getAttributionTag()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setAttributionTag(String)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setFlags(int):
+    android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getFlags()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setFlags(int)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setOpNames(java.util.List<java.lang.String>):
+    android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getOpNames()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setOpNames(java.util.List<java.lang.String>)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setPackageName(String):
+    android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getPackageName()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setPackageName(String)
+MissingGetterMatchingBuilder: android.app.AppOpsManager.HistoricalOpsRequest.Builder#setUid(int):
+    android.app.AppOpsManager.HistoricalOpsRequest does not declare a `getUid()` method matching method android.app.AppOpsManager.HistoricalOpsRequest.Builder.setUid(int)
+MissingGetterMatchingBuilder: android.content.integrity.RuleSet.Builder#addRules(java.util.List<android.content.integrity.Rule>):
+    android.content.integrity.RuleSet does not declare a `getRuless()` method matching method android.content.integrity.RuleSet.Builder.addRules(java.util.List<android.content.integrity.Rule>)
+MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection):
+    android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByCategorys()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByCategory(int,android.hardware.display.BrightnessCorrection)
+MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection):
+    android.hardware.display.BrightnessConfiguration does not declare a `getCorrectionByPackageNames()` method matching method android.hardware.display.BrightnessConfiguration.Builder.addCorrectionByPackageName(String,android.hardware.display.BrightnessCorrection)
+MissingGetterMatchingBuilder: android.hardware.display.BrightnessConfiguration.Builder#setDescription(String):
+    android.hardware.display.BrightnessConfiguration does not declare a `getDescription()` method matching method android.hardware.display.BrightnessConfiguration.Builder.setDescription(String)
+MissingGetterMatchingBuilder: android.hardware.lights.LightsRequest.Builder#setLight(android.hardware.lights.Light, android.hardware.lights.LightState):
+    android.hardware.lights.LightsRequest does not declare a `getLight()` method matching method android.hardware.lights.LightsRequest.Builder.setLight(android.hardware.lights.Light,android.hardware.lights.LightState)
+MissingGetterMatchingBuilder: android.media.VolumeShaper.Configuration.Builder#setOptionFlags(int):
+    android.media.VolumeShaper.Configuration does not declare a `getOptionFlags()` method matching method android.media.VolumeShaper.Configuration.Builder.setOptionFlags(int)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setDevice(android.media.AudioDeviceInfo):
+    android.media.audiopolicy.AudioMix does not declare a `getDevice()` method matching method android.media.audiopolicy.AudioMix.Builder.setDevice(android.media.AudioDeviceInfo)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setFormat(android.media.AudioFormat):
+    android.media.audiopolicy.AudioMix does not declare a `getFormat()` method matching method android.media.audiopolicy.AudioMix.Builder.setFormat(android.media.AudioFormat)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMix.Builder#setRouteFlags(int):
+    android.media.audiopolicy.AudioMix does not declare a `getRouteFlags()` method matching method android.media.audiopolicy.AudioMix.Builder.setRouteFlags(int)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addMixRule(int, Object):
+    android.media.audiopolicy.AudioMixingRule does not declare a `getMixRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addMixRule(int,Object)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioMixingRule.Builder#addRule(android.media.AudioAttributes, int):
+    android.media.audiopolicy.AudioMixingRule does not declare a `getRules()` method matching method android.media.audiopolicy.AudioMixingRule.Builder.addRule(android.media.AudioAttributes,int)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#addMix(android.media.audiopolicy.AudioMix):
+    android.media.audiopolicy.AudioPolicy does not declare a `getMixs()` method matching method android.media.audiopolicy.AudioPolicy.Builder.addMix(android.media.audiopolicy.AudioMix)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener):
+    android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyFocusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener):
+    android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyStatusListener()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback):
+    android.media.audiopolicy.AudioPolicy does not declare a `getAudioPolicyVolumeCallback()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setAudioPolicyVolumeCallback(android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsAudioFocusPolicy(boolean):
+    android.media.audiopolicy.AudioPolicy does not declare a `isIsAudioFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsAudioFocusPolicy(boolean)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setIsTestFocusPolicy(boolean):
+    android.media.audiopolicy.AudioPolicy does not declare a `isIsTestFocusPolicy()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setIsTestFocusPolicy(boolean)
+MissingGetterMatchingBuilder: android.media.audiopolicy.AudioPolicy.Builder#setLooper(android.os.Looper):
+    android.media.audiopolicy.AudioPolicy does not declare a `getLooper()` method matching method android.media.audiopolicy.AudioPolicy.Builder.setLooper(android.os.Looper)
+MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setBytesRemaining(long):
+    android.net.CaptivePortalData does not declare a `getBytesRemaining()` method matching method android.net.CaptivePortalData.Builder.setBytesRemaining(long)
+MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setExpiryTime(long):
+    android.net.CaptivePortalData does not declare a `getExpiryTime()` method matching method android.net.CaptivePortalData.Builder.setExpiryTime(long)
+MissingGetterMatchingBuilder: android.net.CaptivePortalData.Builder#setRefreshTime(long):
+    android.net.CaptivePortalData does not declare a `getRefreshTime()` method matching method android.net.CaptivePortalData.Builder.setRefreshTime(long)
+MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#addCapability(int):
+    android.net.NetworkCapabilities does not declare a `getCapabilitys()` method matching method android.net.NetworkCapabilities.Builder.addCapability(int)
+MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorPackageName(String):
+    android.net.NetworkCapabilities does not declare a `getRequestorPackageName()` method matching method android.net.NetworkCapabilities.Builder.setRequestorPackageName(String)
+MissingGetterMatchingBuilder: android.net.NetworkCapabilities.Builder#setRequestorUid(int):
+    android.net.NetworkCapabilities does not declare a `getRequestorUid()` method matching method android.net.NetworkCapabilities.Builder.setRequestorUid(int)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setShouldShowEntitlementUi(boolean):
+    android.net.TetheringManager.TetheringRequest does not declare a `shouldShowEntitlementUi()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setShouldShowEntitlementUi(boolean)
+MissingGetterMatchingBuilder: android.net.TetheringManager.TetheringRequest.Builder#setStaticIpv4Addresses(android.net.LinkAddress, android.net.LinkAddress):
+    android.net.TetheringManager.TetheringRequest does not declare a `getStaticIpv4Addresses()` method matching method android.net.TetheringManager.TetheringRequest.Builder.setStaticIpv4Addresses(android.net.LinkAddress,android.net.LinkAddress)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setActualLifetime(long):
+    android.net.metrics.ApfProgramEvent does not declare a `getActualLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setActualLifetime(long)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setCurrentRas(int):
+    android.net.metrics.ApfProgramEvent does not declare a `getCurrentRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setCurrentRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFilteredRas(int):
+    android.net.metrics.ApfProgramEvent does not declare a `getFilteredRas()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFilteredRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setFlags(boolean, boolean):
+    android.net.metrics.ApfProgramEvent does not declare a `isFlags()` method matching method android.net.metrics.ApfProgramEvent.Builder.setFlags(boolean,boolean)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setLifetime(long):
+    android.net.metrics.ApfProgramEvent does not declare a `getLifetime()` method matching method android.net.metrics.ApfProgramEvent.Builder.setLifetime(long)
+MissingGetterMatchingBuilder: android.net.metrics.ApfProgramEvent.Builder#setProgramLength(int):
+    android.net.metrics.ApfProgramEvent does not declare a `getProgramLength()` method matching method android.net.metrics.ApfProgramEvent.Builder.setProgramLength(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDroppedRas(int):
+    android.net.metrics.ApfStats does not declare a `getDroppedRas()` method matching method android.net.metrics.ApfStats.Builder.setDroppedRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setDurationMs(long):
+    android.net.metrics.ApfStats does not declare a `getDurationMs()` method matching method android.net.metrics.ApfStats.Builder.setDurationMs(long)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMatchingRas(int):
+    android.net.metrics.ApfStats does not declare a `getMatchingRas()` method matching method android.net.metrics.ApfStats.Builder.setMatchingRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setMaxProgramSize(int):
+    android.net.metrics.ApfStats does not declare a `getMaxProgramSize()` method matching method android.net.metrics.ApfStats.Builder.setMaxProgramSize(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setParseErrors(int):
+    android.net.metrics.ApfStats does not declare a `getParseErrors()` method matching method android.net.metrics.ApfStats.Builder.setParseErrors(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdates(int):
+    android.net.metrics.ApfStats does not declare a `getProgramUpdates()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdates(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAll(int):
+    android.net.metrics.ApfStats does not declare a `getProgramUpdatesAll()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAll(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setProgramUpdatesAllowingMulticast(int):
+    android.net.metrics.ApfStats does not declare a `getProgramUpdatesAllowingMulticast()` method matching method android.net.metrics.ApfStats.Builder.setProgramUpdatesAllowingMulticast(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setReceivedRas(int):
+    android.net.metrics.ApfStats does not declare a `getReceivedRas()` method matching method android.net.metrics.ApfStats.Builder.setReceivedRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.ApfStats.Builder#setZeroLifetimeRas(int):
+    android.net.metrics.ApfStats does not declare a `getZeroLifetimeRas()` method matching method android.net.metrics.ApfStats.Builder.setZeroLifetimeRas(int)
+MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setDurationMs(int):
+    android.net.metrics.DhcpClientEvent does not declare a `getDurationMs()` method matching method android.net.metrics.DhcpClientEvent.Builder.setDurationMs(int)
+MissingGetterMatchingBuilder: android.net.metrics.DhcpClientEvent.Builder#setMsg(String):
+    android.net.metrics.DhcpClientEvent does not declare a `getMsg()` method matching method android.net.metrics.DhcpClientEvent.Builder.setMsg(String)
+MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setDurationMs(long):
+    android.net.metrics.ValidationProbeEvent does not declare a `getDurationMs()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setDurationMs(long)
+MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setProbeType(int, boolean):
+    android.net.metrics.ValidationProbeEvent does not declare a `getProbeType()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setProbeType(int,boolean)
+MissingGetterMatchingBuilder: android.net.metrics.ValidationProbeEvent.Builder#setReturnCode(int):
+    android.net.metrics.ValidationProbeEvent does not declare a `getReturnCode()` method matching method android.net.metrics.ValidationProbeEvent.Builder.setReturnCode(int)
+MissingGetterMatchingBuilder: android.security.keystore.KeyGenParameterSpec.Builder#setUniqueIdIncluded(boolean):
+    android.security.keystore.KeyGenParameterSpec does not declare a `isUniqueIdIncluded()` method matching method android.security.keystore.KeyGenParameterSpec.Builder.setUniqueIdIncluded(boolean)
+MissingGetterMatchingBuilder: android.service.autofill.Dataset.Builder#setFieldInlinePresentation(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.service.autofill.InlinePresentation):
+    android.service.autofill.Dataset does not declare a `getFieldInlinePresentation()` method matching method android.service.autofill.Dataset.Builder.setFieldInlinePresentation(android.view.autofill.AutofillId,android.view.autofill.AutofillValue,java.util.regex.Pattern,android.service.autofill.InlinePresentation)
+MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setClientState(android.os.Bundle):
+    android.service.autofill.augmented.FillResponse does not declare a `getClientState()` method matching method android.service.autofill.augmented.FillResponse.Builder.setClientState(android.os.Bundle)
+MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setFillWindow(android.service.autofill.augmented.FillWindow):
+    android.service.autofill.augmented.FillResponse does not declare a `getFillWindow()` method matching method android.service.autofill.augmented.FillResponse.Builder.setFillWindow(android.service.autofill.augmented.FillWindow)
+MissingGetterMatchingBuilder: android.service.autofill.augmented.FillResponse.Builder#setInlineSuggestions(java.util.List<android.service.autofill.Dataset>):
+    android.service.autofill.augmented.FillResponse does not declare a `getInlineSuggestions()` method matching method android.service.autofill.augmented.FillResponse.Builder.setInlineSuggestions(java.util.List<android.service.autofill.Dataset>)
+MissingGetterMatchingBuilder: android.telecom.CallScreeningService.CallResponse.Builder#setShouldScreenCallViaAudioProcessing(boolean):
+    android.telecom.CallScreeningService.CallResponse does not declare a `shouldScreenCallViaAudioProcessing()` method matching method android.telecom.CallScreeningService.CallResponse.Builder.setShouldScreenCallViaAudioProcessing(boolean)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setIsAdhocConferenceCall(boolean):
+    android.telecom.ConnectionRequest does not declare a `isIsAdhocConferenceCall()` method matching method android.telecom.ConnectionRequest.Builder.setIsAdhocConferenceCall(boolean)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeFromInCall(android.os.ParcelFileDescriptor):
+    android.telecom.ConnectionRequest does not declare a `getRttPipeFromInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeFromInCall(android.os.ParcelFileDescriptor)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setRttPipeToInCall(android.os.ParcelFileDescriptor):
+    android.telecom.ConnectionRequest does not declare a `getRttPipeToInCall()` method matching method android.telecom.ConnectionRequest.Builder.setRttPipeToInCall(android.os.ParcelFileDescriptor)
+MissingGetterMatchingBuilder: android.telecom.ConnectionRequest.Builder#setShouldShowIncomingCallUi(boolean):
+    android.telecom.ConnectionRequest does not declare a `shouldShowIncomingCallUi()` method matching method android.telecom.ConnectionRequest.Builder.setShouldShowIncomingCallUi(boolean)
+MissingGetterMatchingBuilder: android.telecom.PhoneAccount.Builder#setGroupId(String):
+    android.telecom.PhoneAccount does not declare a `getGroupId()` method matching method android.telecom.PhoneAccount.Builder.setGroupId(String)
+MissingGetterMatchingBuilder: android.telephony.NetworkRegistrationInfo.Builder#setEmergencyOnly(boolean):
+    android.telephony.NetworkRegistrationInfo does not declare a `isEmergencyOnly()` method matching method android.telephony.NetworkRegistrationInfo.Builder.setEmergencyOnly(boolean)
+MissingGetterMatchingBuilder: android.telephony.ims.ImsSsData.Builder#setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>):
+    android.telephony.ims.ImsSsData does not declare a `getCallForwardingInfo()` method matching method android.telephony.ims.ImsSsData.Builder.setCallForwardingInfo(java.util.List<android.telephony.ims.ImsCallForwardInfo>)
+MissingGetterMatchingBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int):
+    android.telephony.ims.stub.ImsFeatureConfiguration does not declare a `getFeatures()` method matching method android.telephony.ims.stub.ImsFeatureConfiguration.Builder.addFeature(int,int)
+MissingGetterMatchingBuilder: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
+    android.telephony.mbms.DownloadRequest does not declare a `getServiceId()` method matching method android.telephony.mbms.DownloadRequest.Builder.setServiceId(String)
+
+
 MissingNullability: android.app.Activity#onMovedToDisplay(int, android.content.res.Configuration) parameter #1:
     
 MissingNullability: android.app.ActivityManager#addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int) parameter #0:
@@ -2294,11 +2482,11 @@
 NoClone: android.service.contentcapture.ContentCaptureService#dump(java.io.FileDescriptor, java.io.PrintWriter, String[]) parameter #0:
     
 NoClone: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
-
+    
 
 
 NoSettingsProvider: android.provider.Settings.Global#APP_OPS_CONSTANTS:
-    New setting keys are not allowed (Field: APP_OPS_CONSTANTS); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Global#AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES:
     
 NoSettingsProvider: android.provider.Settings.Global#AUTOMATIC_POWER_SAVE_MODE:
@@ -2312,7 +2500,7 @@
 NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS:
     
 NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS:
-    New setting keys are not allowed (Field: HIDE_ERROR_DIALOGS); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH:
     
 NoSettingsProvider: android.provider.Settings.Global#LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST:
@@ -2356,21 +2544,21 @@
 NoSettingsProvider: android.provider.Settings.Secure#ENABLED_VR_LISTENERS:
     
 NoSettingsProvider: android.provider.Settings.Secure#IMMERSIVE_MODE_CONFIRMATIONS:
-    New setting keys are not allowed (Field: IMMERSIVE_MODE_CONFIRMATIONS); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_DELAY_MILLIS:
     
 NoSettingsProvider: android.provider.Settings.Secure#LOCATION_ACCESS_CHECK_INTERVAL_MILLIS:
     
 NoSettingsProvider: android.provider.Settings.Secure#LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS:
-    New setting keys are not allowed (Field: LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Secure#LOCK_SCREEN_SHOW_NOTIFICATIONS:
-    New setting keys are not allowed (Field: LOCK_SCREEN_SHOW_NOTIFICATIONS); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Secure#NFC_PAYMENT_DEFAULT_COMPONENT:
-    New setting keys are not allowed (Field: NFC_PAYMENT_DEFAULT_COMPONENT); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Secure#NOTIFICATION_BADGING:
     
 NoSettingsProvider: android.provider.Settings.Secure#POWER_MENU_LOCKED_SHOW_CONTENT:
-    New setting keys are not allowed (Field: POWER_MENU_LOCKED_SHOW_CONTENT); use getters/setters in relevant manager class
+    
 NoSettingsProvider: android.provider.Settings.Secure#SYNC_PARENT_SOUNDS:
     
 NoSettingsProvider: android.provider.Settings.Secure#USER_SETUP_COMPLETE:
@@ -2384,7 +2572,7 @@
 NotCloseable: android.app.prediction.AppPredictor:
     
 NotCloseable: android.net.EthernetManager.TetheredInterfaceRequest:
-    Classes that release resources (release()) should implement AutoClosable and CloseGuard: class android.net.EthernetManager.TetheredInterfaceRequest
+    
 NotCloseable: android.os.HwParcel:
     
 NotCloseable: android.telephony.ims.stub.ImsUtImplBase:
@@ -2401,6 +2589,8 @@
     
 OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported():
     
+OnNameExpected: android.service.watchdog.ExplicitHealthCheckService#setCallback(android.os.RemoteCallback):
+    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
 OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int):
     
 OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int):
@@ -2429,6 +2619,14 @@
     
 
 
+OptionalBuilderConstructorArgument: android.app.prediction.AppTargetEvent.Builder#Builder(android.app.prediction.AppTarget, int) parameter #0:
+    Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter target in android.app.prediction.AppTargetEvent.Builder(android.app.prediction.AppTarget target, int actionType)
+OptionalBuilderConstructorArgument: android.net.CaptivePortalData.Builder#Builder(android.net.CaptivePortalData) parameter #0:
+    Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter data in android.net.CaptivePortalData.Builder(android.net.CaptivePortalData data)
+OptionalBuilderConstructorArgument: android.os.VibrationAttributes.Builder#Builder(android.media.AudioAttributes, android.os.VibrationEffect) parameter #1:
+    Builder constructor arguments must be mandatory (i.e. not @Nullable): parameter effect in android.os.VibrationAttributes.Builder(android.media.AudioAttributes audio, android.os.VibrationEffect effect)
+
+
 PackageLayering: android.util.FeatureFlagUtils:
     
 
@@ -2625,6 +2823,8 @@
     
 SamShouldBeLast: android.os.StrictMode.ViolationInfo#dump(android.util.Printer, String):
     
+SamShouldBeLast: android.permission.PermissionControllerManager#countPermissionApps(java.util.List<java.lang.String>, int, android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, android.os.Handler):
+    SAM-compatible parameters (such as parameter 3, "callback", in android.permission.PermissionControllerManager.countPermissionApps) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
 SamShouldBeLast: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
     
 SamShouldBeLast: android.permission.PermissionControllerManager#revokeRuntimePermissions(java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, java.util.concurrent.Executor, android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback):
@@ -2669,6 +2869,24 @@
     
 
 
+StaticFinalBuilder: android.content.integrity.RuleSet.Builder:
+    Builder must be final: android.content.integrity.RuleSet.Builder
+StaticFinalBuilder: android.hardware.display.BrightnessConfiguration.Builder:
+    Builder must be final: android.hardware.display.BrightnessConfiguration.Builder
+StaticFinalBuilder: android.media.audiopolicy.AudioMix.Builder:
+    Builder must be final: android.media.audiopolicy.AudioMix.Builder
+StaticFinalBuilder: android.media.audiopolicy.AudioMixingRule.Builder:
+    Builder must be final: android.media.audiopolicy.AudioMixingRule.Builder
+StaticFinalBuilder: android.media.audiopolicy.AudioPolicy.Builder:
+    Builder must be final: android.media.audiopolicy.AudioPolicy.Builder
+StaticFinalBuilder: android.net.CaptivePortalData.Builder:
+    Builder must be final: android.net.CaptivePortalData.Builder
+StaticFinalBuilder: android.net.TetheringManager.TetheringRequest.Builder:
+    Builder must be final: android.net.TetheringManager.TetheringRequest.Builder
+StaticFinalBuilder: android.telephony.ims.stub.ImsFeatureConfiguration.Builder:
+    Builder must be final: android.telephony.ims.stub.ImsFeatureConfiguration.Builder
+
+
 StaticUtils: android.os.health.HealthKeys:
     
 StaticUtils: android.service.autofill.InternalTransformation:
@@ -2693,6 +2911,18 @@
     
 
 
+UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getKeyphraseMetadata(String, java.util.Locale) parameter #1:
+    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.KeyphraseEnrollmentInfo#getManageKeyphraseIntent(int, String, java.util.Locale) parameter #2:
+    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.KeyphraseMetadata#supportsLocale(java.util.Locale) parameter #0:
+    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#Keyphrase(int, int, java.util.Locale, String, int[]) parameter #2:
+    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+UseIcu: android.hardware.soundtrigger.SoundTrigger.Keyphrase#getLocale():
+    Type `java.util.Locale` should be replaced with richer ICU type `android.icu.util.ULocale`
+
+
 UseParcelFileDescriptor: android.util.proto.ProtoOutputStream#ProtoOutputStream(java.io.FileDescriptor) parameter #0:
     
 
@@ -2720,7 +2950,7 @@
 UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
     
 UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociatedForWifiConnection(String, android.net.MacAddress, android.os.UserHandle):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+    
 UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
     
 UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle):
@@ -2730,7 +2960,7 @@
 UserHandle: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle):
     
 UserHandle: android.content.pm.PackageManager#revokeRuntimePermission(String, String, android.os.UserHandle, String):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+    
 UserHandle: android.content.pm.PackageManager#updatePermissionFlags(String, String, int, int, android.os.UserHandle):
     
 UserHandle: android.location.LocationManager#setLocationEnabledForUser(boolean, android.os.UserHandle):
@@ -2742,7 +2972,7 @@
 UserHandle: android.permission.PermissionControllerManager#stageAndApplyRuntimePermissionsBackup(byte[], android.os.UserHandle):
     
 UserHandle: android.telecom.TelecomManager#getDefaultDialerPackage(android.os.UserHandle):
-    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
+    
 
 
 UserHandleName: android.app.ActivityView#startActivity(android.content.Intent, android.os.UserHandle):
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b50af38..8c54c24 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -587,6 +587,8 @@
         BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
                 10083 [(module) = "framework"];
         DNDModeProto dnd_mode_rule = 10084 [(module) = "framework"];
+        GeneralExternalStorageAccessStats general_external_storage_access_stats =
+            10085 [(module) = "mediaprovider"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -4566,6 +4568,31 @@
     optional State state  = 2;
 }
 
+message MimeTypes {
+    repeated string mime_types = 1;
+}
+
+/**
+ * Logs statistics regarding accesses to external storage.
+ * All stats are normalized for one day period.
+ *
+ * Logged from:
+ *   packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message GeneralExternalStorageAccessStats {
+    optional int32 uid = 1 [(is_uid) = true];
+    // Total number of accesses like creation, open, delete and rename/update.
+    // Includes file path and ContentResolver accesses
+    optional uint32 total_accesses = 2;
+    // Number of file path accesses, as opposed to file path and ContentResolver.
+    optional uint32 file_path_accesses = 3;
+    // Number of accesses on secondary volumes like SD cards.
+    // Includes file path and ContentResolver accesses
+    optional uint32 secondary_storage_accesses = 4;
+    // Comma-separated list of mime types that were accessed.
+    optional MimeTypes mime_types_accessed = 5;
+}
+
 /**
  * Logs when MediaProvider has successfully finished scanning a storage volume.
  *
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index 6384fb1..51bcad1 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -342,6 +342,9 @@
                     .addPullAtomPackages(PullAtomPackages.newBuilder()
                             .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
                             .addPackages("AID_STATSD"))
+                    .addPullAtomPackages(PullAtomPackages.newBuilder()
+                            .setAtomId(Atom.GENERAL_EXTERNAL_STORAGE_ACCESS_STATS_FIELD_NUMBER)
+                            .addPackages("com.google.android.providers.media.module"))
                     .setHashStringsInMetricReport(false);
         }
     }
diff --git a/config/preloaded-classes b/config/preloaded-classes
index a11f1a1..0bff3aa 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -9990,8 +9990,6 @@
 dalvik.system.CloseGuard$Reporter
 dalvik.system.CloseGuard$Tracker
 dalvik.system.CloseGuard
-dalvik.system.DalvikLogHandler
-dalvik.system.DalvikLogging
 dalvik.system.DelegateLastClassLoader
 dalvik.system.DexClassLoader
 dalvik.system.DexFile$1
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index adf5fa5..9849e59 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1900,11 +1900,13 @@
 
     @Override
     public Object getSystemService(String name) {
+        // We may override this API from outer context.
+        final boolean isUiContext = isUiContext() || getOuterContext().isUiContext();
         // Check incorrect Context usage.
-        if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
+        if (isUiComponent(name) && !isUiContext && vmIncorrectContextUseEnabled()) {
             final String errorMessage = "Tried to access visual service "
                     + SystemServiceRegistry.getSystemServiceClassName(name)
-                    + " from a non-visual Context. ";
+                    + " from a non-visual Context:" + getOuterContext();
             final String message = "Visual services, such as WindowManager, WallpaperService or "
                     + "LayoutInflater should be accessed from Activity or other visual Context. "
                     + "Use an Activity or a Context created with "
@@ -2370,6 +2372,7 @@
         context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
                 mResources.getLoaders()));
+        context.mIsUiContext = isUiContext() || getOuterContext().isUiContext();
         return context;
     }
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 03b941d..3410c92 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -96,9 +96,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -1620,7 +1620,7 @@
          * of non-textual RemoteInputs do not access these remote inputs.
          */
         public RemoteInput[] getDataOnlyRemoteInputs() {
-            return (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+            return getParcelableArrayFromBundle(mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class);
         }
 
         /**
@@ -1802,8 +1802,8 @@
                 checkContextualActionNullFields();
 
                 ArrayList<RemoteInput> dataOnlyInputs = new ArrayList<>();
-                RemoteInput[] previousDataInputs =
-                    (RemoteInput[]) mExtras.getParcelableArray(EXTRA_DATA_ONLY_INPUTS);
+                RemoteInput[] previousDataInputs = getParcelableArrayFromBundle(
+                        mExtras, EXTRA_DATA_ONLY_INPUTS, RemoteInput.class);
                 if (previousDataInputs != null) {
                     for (RemoteInput input : previousDataInputs) {
                         dataOnlyInputs.add(input);
@@ -5374,8 +5374,8 @@
                 big.setViewVisibility(R.id.actions_container, View.GONE);
             }
 
-            RemoteInputHistoryItem[] replyText = (RemoteInputHistoryItem[])
-                    mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+            RemoteInputHistoryItem[] replyText = getParcelableArrayFromBundle(
+                    mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS, RemoteInputHistoryItem.class);
             if (validRemoteInput && replyText != null && replyText.length > 0
                     && !TextUtils.isEmpty(replyText[0].getText())
                     && p.maxRemoteInputHistory > 0) {
@@ -8161,8 +8161,9 @@
             if (mBuilder.mActions.size() > 0) {
                 maxRows--;
             }
-            RemoteInputHistoryItem[] remoteInputHistory = (RemoteInputHistoryItem[])
-                    mBuilder.mN.extras.getParcelableArray(EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+            RemoteInputHistoryItem[] remoteInputHistory = getParcelableArrayFromBundle(
+                    mBuilder.mN.extras, EXTRA_REMOTE_INPUT_HISTORY_ITEMS,
+                    RemoteInputHistoryItem.class);
             if (remoteInputHistory != null
                     && remoteInputHistory.length > NUMBER_OF_HISTORY_ALLOWED_UNTIL_REDUCTION) {
                 // Let's remove some messages to make room for the remote input history.
@@ -9585,8 +9586,8 @@
                 mFlags = wearableBundle.getInt(KEY_FLAGS, DEFAULT_FLAGS);
                 mDisplayIntent = wearableBundle.getParcelable(KEY_DISPLAY_INTENT);
 
-                Notification[] pages = getNotificationArrayFromBundle(
-                        wearableBundle, KEY_PAGES);
+                Notification[] pages = getParcelableArrayFromBundle(
+                        wearableBundle, KEY_PAGES, Notification.class);
                 if (pages != null) {
                     Collections.addAll(mPages, pages);
                 }
@@ -10844,17 +10845,22 @@
     }
 
     /**
-     * Get an array of Notification objects from a parcelable array bundle field.
+     * Get an array of Parcelable objects from a parcelable array bundle field.
      * Update the bundle to have a typed array so fetches in the future don't need
      * to do an array copy.
      */
-    private static Notification[] getNotificationArrayFromBundle(Bundle bundle, String key) {
-        Parcelable[] array = bundle.getParcelableArray(key);
-        if (array instanceof Notification[] || array == null) {
-            return (Notification[]) array;
+    @Nullable
+    private static <T extends Parcelable> T[] getParcelableArrayFromBundle(
+            Bundle bundle, String key, Class<T> itemClass) {
+        final Parcelable[] array = bundle.getParcelableArray(key);
+        final Class<?> arrayClass = Array.newInstance(itemClass, 0).getClass();
+        if (arrayClass.isInstance(array) || array == null) {
+            return (T[]) array;
         }
-        Notification[] typedArray = Arrays.copyOf(array, array.length,
-                Notification[].class);
+        final T[] typedArray = (T[]) Array.newInstance(itemClass, array.length);
+        for (int i = 0; i < array.length; i++) {
+            typedArray[i] = (T) array[i];
+        }
         bundle.putParcelableArray(key, typedArray);
         return typedArray;
     }
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 03af918..33f32fb 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -49,7 +49,6 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
-import android.os.AndroidTimeoutException;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.DeadObjectException;
@@ -2189,8 +2188,14 @@
             return null;
         }
 
-        try (ContentProviderClient provider = acquireRequiredContentProviderClient(authority)) {
+        ContentProviderClient provider = acquireContentProviderClient(authority);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown authority " + authority);
+        }
+        try {
             return provider.applyBatch(operations);
+        } finally {
+            provider.release();
         }
     }
 
@@ -2392,7 +2397,10 @@
             return null;
         }
 
-        IContentProvider provider = acquireRequiredProvider(authority);
+        IContentProvider provider = acquireProvider(authority);
+        if (provider == null) {
+            throw new IllegalArgumentException("Unknown authority " + authority);
+        }
         try {
             final Bundle res = provider.call(mPackageName, mAttributionTag, authority, method, arg,
                     extras);
@@ -2411,9 +2419,7 @@
      * Returns the content provider for the given content URI.
      *
      * @param uri The URI to a content provider
-     * @return The ContentProvider for the given URI, or null if the content provider initialization
-     * times out.
-     *
+     * @return The ContentProvider for the given URI, or null if no content provider is found.
      * @hide
      */
     @UnsupportedAppUsage
@@ -2423,32 +2429,12 @@
         }
         final String auth = uri.getAuthority();
         if (auth != null) {
-            try {
-                return acquireProvider(mContext, auth);
-            } catch (AndroidTimeoutException ate) {
-                Log.e(TAG, ate.getMessage());
-                return null;
-            }
+            return acquireProvider(mContext, auth);
         }
         return null;
     }
 
     /**
-     * Same as {@link #acquireProvider(String)}, except it throws an exception
-     * if the content provider does not exist or times out on start.
-     *
-     * @hide
-     */
-    @NonNull
-    private IContentProvider acquireRequiredProvider(@NonNull String name) {
-        IContentProvider provider = acquireProvider(mContext, name);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown authority " + name);
-        }
-        return provider;
-    }
-
-    /**
      * Returns the content provider for the given content URI if the process
      * already has a reference on it.
      *
@@ -2476,12 +2462,7 @@
         if (name == null) {
             return null;
         }
-        try {
-            return acquireProvider(mContext, name);
-        } catch (AndroidTimeoutException ate) {
-            Log.e(TAG, ate.getMessage());
-            return null;
-        }
+        return acquireProvider(mContext, name);
     }
 
     /**
@@ -2556,19 +2537,6 @@
     }
 
     /**
-     * Same as {@link #acquireContentProviderClient(String)}, except it throws an exception
-     * if the content provider does not exist or times out on start.
-     *
-     * @hide
-     */
-    private @NonNull ContentProviderClient acquireRequiredContentProviderClient(
-            @NonNull String name) {
-        Objects.requireNonNull(name, "name");
-        IContentProvider provider = acquireRequiredProvider(name);
-        return new ContentProviderClient(this, provider, name, true);
-    }
-
-    /**
      * Like {@link #acquireContentProviderClient(Uri)}, but for use when you do
      * not trust the stability of the target content provider.  This turns off
      * the mechanism in the platform clean up processes that are dependent on
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index c2ae7f1..df9db27 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -1292,9 +1292,8 @@
          *
          * @throws PackageManager.NameNotFoundException if the new owner could not be found.
          * @throws SecurityException if called after the session has been committed or abandoned.
-         * @throws SecurityException if the session does not update the original installer
-         * @throws SecurityException if streams opened through
-         *                           {@link #openWrite(String, long, long) are still open.
+         * @throws IllegalArgumentException if streams opened through
+         *                                  {@link #openWrite(String, long, long) are still open.
          */
         public void transfer(@NonNull String packageName)
                 throws PackageManager.NameNotFoundException {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d0f9187..940bf26 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1494,12 +1494,12 @@
     public static final int INSTALL_FAILED_ABORTED = -115;
 
     /**
-     * Installation failed return code: instant app installs are incompatible with some
-     * other installation flags supplied for the operation; or other circumstances such
-     * as trying to upgrade a system app via an instant app install.
+     * Installation failed return code: install type is incompatible with some other
+     * installation flags supplied for the operation; or other circumstances such as trying
+     * to upgrade a system app via an Incremental or instant app install.
      * @hide
      */
-    public static final int INSTALL_FAILED_INSTANT_APP_INVALID = -116;
+    public static final int INSTALL_FAILED_SESSION_INVALID = -116;
 
     /**
      * Installation parse return code: this is passed in the
@@ -7474,6 +7474,7 @@
             case INSTALL_FAILED_BAD_SIGNATURE: return "INSTALL_FAILED_BAD_SIGNATURE";
             case INSTALL_FAILED_WRONG_INSTALLED_VERSION: return "INSTALL_FAILED_WRONG_INSTALLED_VERSION";
             case INSTALL_FAILED_PROCESS_NOT_DEFINED: return "INSTALL_FAILED_PROCESS_NOT_DEFINED";
+            case INSTALL_FAILED_SESSION_INVALID: return "INSTALL_FAILED_SESSION_INVALID";
             default: return Integer.toString(status);
         }
     }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5ca55c7..72246ee 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1704,13 +1704,6 @@
                         minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
                     }
                 }
-            } else if (TAG_PROFILEABLE.equals(parser.getName())) {
-                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
-                    final String attr = attrs.getAttributeName(i);
-                    if ("shell".equals(attr)) {
-                        profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
-                    }
-                }
             }
         }
 
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 4800f89..4914601 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -398,6 +398,30 @@
                             break;
                     }
                 }
+
+                final int innerDepth = parser.getDepth();
+                int innerType;
+                while ((innerType = parser.next()) != XmlPullParser.END_DOCUMENT
+                        && (innerType != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
+                    if (innerType == XmlPullParser.END_TAG || innerType == XmlPullParser.TEXT) {
+                        continue;
+                    }
+
+                    if (parser.getDepth() != innerDepth + 1) {
+                        // Search only under <application>.
+                        continue;
+                    }
+
+                    if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+                        for (int i = 0; i < attrs.getAttributeCount(); ++i) {
+                            final String attr = attrs.getAttributeName(i);
+                            if ("shell".equals(attr)) {
+                                profilableByShell = attrs.getAttributeBooleanValue(i,
+                                        profilableByShell);
+                            }
+                        }
+                    }
+                }
             } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
                 for (int i = 0; i < attrs.getAttributeCount(); ++i) {
                     final String attr = attrs.getAttributeName(i);
@@ -435,13 +459,6 @@
                         minSdkVersion = attrs.getAttributeIntValue(i, DEFAULT_MIN_SDK_VERSION);
                     }
                 }
-            } else if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
-                for (int i = 0; i < attrs.getAttributeCount(); ++i) {
-                    final String attr = attrs.getAttributeName(i);
-                    if ("shell".equals(attr)) {
-                        profilableByShell = attrs.getAttributeBooleanValue(i, profilableByShell);
-                    }
-                }
             }
         }
 
diff --git a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
index 4a46972..8eb22da 100644
--- a/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricAuthenticator.aidl
@@ -50,6 +50,9 @@
     // Determine if a user has at least one enrolled face
     boolean hasEnrolledTemplates(int userId, String opPackageName);
 
+    // Return the LockoutTracker status for the specified user
+    int getLockoutModeForUser(int userId);
+
     // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
     void resetLockout(int userId, in byte [] hardwareAuthToken);
 
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 9876de1..58cb1e1 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -165,7 +165,6 @@
      *
      * @param crypto   object associated with the call or null if none required.
      * @param cancel   an object that can be used to cancel authentication
-     * @param flags    optional flags; should be 0
      * @param callback an object to receive authentication events
      * @param handler  an optional handler to handle callback events
      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
@@ -177,8 +176,8 @@
      */
     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
-        authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
+            @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
+        authenticate(crypto, cancel, callback, handler, mContext.getUserId());
     }
 
     /**
@@ -202,7 +201,6 @@
      *
      * @param crypto   object associated with the call or null if none required.
      * @param cancel   an object that can be used to cancel authentication
-     * @param flags    optional flags; should be 0
      * @param callback an object to receive authentication events
      * @param handler  an optional handler to handle callback events
      * @param userId   userId to authenticate for
@@ -214,8 +212,7 @@
      * @hide
      */
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler,
-            int userId) {
+            @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
         }
@@ -237,7 +234,7 @@
                 final long operationId = crypto != null ? crypto.getOpId() : 0;
                 Trace.beginSection("FaceManager#authenticate");
                 mService.authenticate(mToken, operationId, userId, mServiceReceiver,
-                        flags, mContext.getOpPackageName());
+                        mContext.getOpPackageName());
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception while authenticating: ", e);
                 if (callback != null) {
@@ -260,9 +257,9 @@
      * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface)
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void enroll(int userId, byte[] token, CancellationSignal cancel,
+    public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
             EnrollmentCallback callback, int[] disabledFeatures) {
-        enroll(userId, token, cancel, callback, disabledFeatures, null /* surface */);
+        enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */);
     }
 
     /**
@@ -277,7 +274,6 @@
      * @param token    a unique token provided by a recent creation or verification of device
      *                 credentials (e.g. pin, pattern or password).
      * @param cancel   an object that can be used to cancel enrollment
-     * @param flags    optional flags
      * @param userId   the user to whom this face will belong to
      * @param callback an object to receive enrollment events
      * @param surface  optional camera preview surface for a single-camera device. Must be null if
@@ -285,7 +281,7 @@
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void enroll(int userId, byte[] token, CancellationSignal cancel,
+    public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
             EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -304,7 +300,7 @@
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enroll");
-                mService.enroll(userId, mToken, token, mServiceReceiver,
+                mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
                         mContext.getOpPackageName(), disabledFeatures, surface);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
@@ -329,15 +325,15 @@
      * which point the object is no longer valid. The operation can be canceled by using the
      * provided cancel object.
      *
-     * @param token    a unique token provided by a recent creation or verification of device
-     *                 credentials (e.g. pin, pattern or password).
+     * @param hardwareAuthToken    a unique token provided by a recent creation or verification of
+     *                 device credentials (e.g. pin, pattern or password).
      * @param cancel   an object that can be used to cancel enrollment
      * @param userId   the user to whom this face will belong to
      * @param callback an object to receive enrollment events
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void enrollRemotely(int userId, byte[] token, CancellationSignal cancel,
+    public void enrollRemotely(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
             EnrollmentCallback callback, int[] disabledFeatures) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -356,7 +352,7 @@
             try {
                 mEnrollmentCallback = callback;
                 Trace.beginSection("FaceManager#enrollRemotely");
-                mService.enrollRemotely(userId, mToken, token, mServiceReceiver,
+                mService.enrollRemotely(userId, mToken, hardwareAuthToken, mServiceReceiver,
                         mContext.getOpPackageName(), disabledFeatures);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
@@ -444,13 +440,13 @@
      * @hide
      */
     @RequiresPermission(MANAGE_BIOMETRIC)
-    public void setFeature(int userId, int feature, boolean enabled, byte[] token,
+    public void setFeature(int userId, int feature, boolean enabled, byte[] hardwareAuthToken,
             SetFeatureCallback callback) {
         if (mService != null) {
             try {
                 mSetFeatureCallback = callback;
-                mService.setFeature(userId, feature, enabled, token, mServiceReceiver,
-                        mContext.getOpPackageName());
+                mService.setFeature(mToken, userId, feature, enabled, hardwareAuthToken,
+                        mServiceReceiver, mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -465,22 +461,8 @@
         if (mService != null) {
             try {
                 mGetFeatureCallback = callback;
-                mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName());
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
-     * Pokes the the driver to have it start looking for faces again.
-     * @hide
-     */
-    @RequiresPermission(MANAGE_BIOMETRIC)
-    public void userActivity() {
-        if (mService != null) {
-            try {
-                mService.userActivity();
+                mService.getFeature(mToken, userId, feature, mServiceReceiver,
+                        mContext.getOpPackageName());
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 8dbaf21..f4122fe 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -28,8 +28,8 @@
  */
 interface IFaceService {
     // Authenticate the given sessionId with a face
-    void authenticate(IBinder token, long operationId, int userid,
-            IFaceServiceReceiver receiver, int flags, String opPackageName);
+    void authenticate(IBinder token, long operationId, int userid, IFaceServiceReceiver receiver,
+            String opPackageName);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
     // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
@@ -51,11 +51,11 @@
             int callingUid, int callingPid, int callingUserId);
 
     // Start face enrollment
-    void enroll(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+    void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
             String opPackageName, in int [] disabledFeatures, in Surface surface);
 
     // Start remote face enrollment
-    void enrollRemotely(int userId, IBinder token, in byte [] cryptoToken, IFaceServiceReceiver receiver,
+    void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
             String opPackageName, in int [] disabledFeatures);
 
     // Cancel enrollment in progress
@@ -80,21 +80,23 @@
     // Determine if a user has at least one enrolled face
     boolean hasEnrolledFaces(int userId, String opPackageName);
 
+    // Return the LockoutTracker status for the specified user
+    int getLockoutModeForUser(int userId);
+
     // Gets the authenticator ID for face
     long getAuthenticatorId(int callingUserId);
 
     // Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
-    void resetLockout(int userId, in byte [] token);
+    void resetLockout(int userId, in byte [] hardwareAuthToken);
 
     // Add a callback which gets notified when the face lockout period expired.
     void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
 
-    void setFeature(int userId, int feature, boolean enabled, in byte [] token,
-            IFaceServiceReceiver receiver, String opPackageName);
+    void setFeature(IBinder token, int userId, int feature, boolean enabled,
+            in byte [] hardwareAuthToken, IFaceServiceReceiver receiver, String opPackageName);
 
-    void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName);
-
-    void userActivity();
+    void getFeature(IBinder token, int userId, int feature, IFaceServiceReceiver receiver,
+            String opPackageName);
 
     // Give FaceService its ID. See AuthService.java
     void initializeConfiguration(int sensorId);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 4787984..abe63d6 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -277,7 +277,7 @@
 
     /**
      * Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
-     * int, int, EnrollmentCallback)} must provide an implementation of this for listening to
+     * int, EnrollmentCallback)} must provide an implementation of this for listening to
      * fingerprint events.
      *
      * @hide
@@ -387,7 +387,7 @@
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
-        authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
+        authenticate(crypto, cancel, callback, handler, mContext.getUserId());
     }
 
     /**
@@ -403,18 +403,18 @@
     }
 
     /**
-     * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal, int,
+     * Defaults to {@link FingerprintManager#authenticate(CryptoObject, CancellationSignal,
      * AuthenticationCallback, Handler, int, Surface)} with {@code surface} set to null.
      *
-     * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal, int,
+     * @see FingerprintManager#authenticate(CryptoObject, CancellationSignal,
      * AuthenticationCallback, Handler, int, Surface)
      *
      * @hide
      */
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
-        authenticate(crypto, cancel, flags, callback, handler, userId, null /* surface */);
+            @NonNull AuthenticationCallback callback, Handler handler, int userId) {
+        authenticate(crypto, cancel, callback, handler, userId, null /* surface */);
     }
 
     /**
@@ -428,7 +428,7 @@
      */
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
-            int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId,
+            @NonNull AuthenticationCallback callback, Handler handler, int userId,
             @Nullable Surface surface) {
         if (callback == null) {
             throw new IllegalArgumentException("Must supply an authentication callback");
@@ -449,7 +449,7 @@
                 mAuthenticationCallback = callback;
                 mCryptoObject = crypto;
                 final long operationId = crypto != null ? crypto.getOpId() : 0;
-                mService.authenticate(mToken, operationId, userId, mServiceReceiver, flags,
+                mService.authenticate(mToken, operationId, userId, mServiceReceiver,
                         mContext.getOpPackageName(), surface);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception while authenticating: ", e);
@@ -463,18 +463,18 @@
     }
 
     /**
-     * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int, int,
+     * Defaults to {@link FingerprintManager#enroll(byte[], CancellationSignal, int,
      * EnrollmentCallback, Surface)} with {@code surface} set to null.
      *
-     * @see FingerprintManager#enroll(byte[], CancellationSignal, int, int, EnrollmentCallback,
+     * @see FingerprintManager#enroll(byte[], CancellationSignal, int, EnrollmentCallback,
      * Surface)
      *
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void enroll(byte [] token, CancellationSignal cancel, int flags,
-            int userId, EnrollmentCallback callback) {
-        enroll(token, cancel, flags, userId, callback, null /* surface */);
+    public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
+            EnrollmentCallback callback) {
+        enroll(hardwareAuthToken, cancel, userId, callback, null /* surface */);
     }
 
     /**
@@ -488,14 +488,13 @@
      * @param token a unique token provided by a recent creation or verification of device
      * credentials (e.g. pin, pattern or password).
      * @param cancel an object that can be used to cancel enrollment
-     * @param flags optional flags
      * @param userId the user to whom this fingerprint will belong to
      * @param callback an object to receive enrollment events
      * @hide
      */
     @RequiresPermission(MANAGE_FINGERPRINT)
-    public void enroll(byte [] token, CancellationSignal cancel, int flags,
-            int userId, EnrollmentCallback callback, @Nullable Surface surface) {
+    public void enroll(byte [] hardwareAuthToken, CancellationSignal cancel, int userId,
+            EnrollmentCallback callback, @Nullable Surface surface) {
         if (userId == UserHandle.USER_CURRENT) {
             userId = getCurrentUserId();
         }
@@ -515,7 +514,7 @@
         if (mService != null) {
             try {
                 mEnrollmentCallback = callback;
-                mService.enroll(mToken, token, userId, mServiceReceiver, flags,
+                mService.enroll(mToken, hardwareAuthToken, userId, mServiceReceiver,
                         mContext.getOpPackageName(), surface);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception in enroll: ", e);
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index b72881a..5adba75 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -19,6 +19,7 @@
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.hardware.fingerprint.Fingerprint;
 import android.view.Surface;
 import java.util.List;
@@ -32,8 +33,7 @@
     // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
     // through FingerprintManager now.
     void authenticate(IBinder token, long operationId, int userId,
-            IFingerprintServiceReceiver receiver, int flags, String opPackageName,
-            in Surface surface);
+            IFingerprintServiceReceiver receiver, String opPackageName, in Surface surface);
 
     // This method prepares the service to start authenticating, but doesn't start authentication.
     // This is protected by the MANAGE_BIOMETRIC signatuer permission. This method should only be
@@ -56,8 +56,8 @@
             int callingUid, int callingPid, int callingUserId);
 
     // Start fingerprint enrollment
-    void enroll(IBinder token, in byte [] cryptoToken, int userId, IFingerprintServiceReceiver receiver,
-            int flags, String opPackageName, in Surface surface);
+    void enroll(IBinder token, in byte [] hardwareAuthToken, int userId, IFingerprintServiceReceiver receiver,
+            String opPackageName, in Surface surface);
 
     // Cancel enrollment in progress
     void cancelEnrollment(IBinder token);
@@ -84,11 +84,14 @@
     // Determine if a user has at least one enrolled fingerprint
     boolean hasEnrolledFingerprints(int userId, String opPackageName);
 
+    // Return the LockoutTracker status for the specified user
+    int getLockoutModeForUser(int userId);
+
     // Gets the authenticator ID for fingerprint
     long getAuthenticatorId(int callingUserId);
 
     // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
-    void resetLockout(int userId, in byte [] cryptoToken);
+    void resetLockout(int userId, in byte [] hardwareAuthToken);
 
     // Add a callback which gets notified when the fingerprint lockout period expired.
     void addLockoutResetCallback(IBiometricServiceLockoutResetCallback callback);
@@ -104,4 +107,22 @@
 
     // Give FingerprintService its ID. See AuthService.java
     void initializeConfiguration(int sensorId);
+
+    // Notifies about a finger touching the sensor area.
+    void onFingerDown(int x, int y, float minor, float major);
+
+    // Notifies about a finger leaving the sensor area.
+    void onFingerUp();
+
+    // Returns whether the specified sensor is an under-display fingerprint sensor (UDFPS).
+    boolean isUdfps(int sensorId);
+
+    // Shows the UDFPS overlay.
+    void showUdfpsOverlay();
+
+    // Hides the UDFPS overlay.
+    void hideUdfpsOverlay();
+
+    // Sets the controller for managing the UDFPS overlay.
+    void setUdfpsOverlayController(in IUdfpsOverlayController controller);
 }
diff --git a/core/java/android/os/AndroidTimeoutException.java b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
similarity index 60%
rename from core/java/android/os/AndroidTimeoutException.java
rename to core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
index fdfcf8d..32530da 100644
--- a/core/java/android/os/AndroidTimeoutException.java
+++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl
@@ -13,21 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-package android.os;
-
-import android.annotation.NonNull;
-import android.util.AndroidRuntimeException;
+package android.hardware.fingerprint;
 
 /**
- * An exception that indicates that the request timed out, for example because
- * the requested content provider took too long to initialize.
- *
+ * Interface for interacting with the under-display fingerprint sensor (UDFPS) overlay.
  * @hide
  */
-public final class AndroidTimeoutException extends AndroidRuntimeException {
+oneway interface IUdfpsOverlayController {
+    // Shows the overlay.
+    void showUdfpsOverlay();
 
-    public AndroidTimeoutException(@NonNull String message) {
-        super(message);
-    }
+    // Hides the overlay.
+    void hideUdfpsOverlay();
 }
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index fc88f1e..2579ee6 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -37,6 +37,7 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.ConcurrentUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -891,6 +892,11 @@
      * <p>To stop getting the notification,
      * use {@link #removeHotplugEventListener(HotplugEventListener)}.
      *
+     * Note that each invocation of the callback will be executed on an arbitrary
+     * Binder thread. This means that all callback implementations must be
+     * thread safe. To specify the execution thread, use
+     * {@link addHotplugEventListener(Executor, HotplugEventListener)}.
+     *
      * @param listener {@link HotplugEventListener} instance
      * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
      *
@@ -899,6 +905,24 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
     public void addHotplugEventListener(HotplugEventListener listener) {
+        addHotplugEventListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
+    }
+
+    /**
+     * Adds a listener to get informed of {@link HdmiHotplugEvent}.
+     *
+     * <p>To stop getting the notification,
+     * use {@link #removeHotplugEventListener(HotplugEventListener)}.
+     *
+     * @param listener {@link HotplugEventListener} instance
+     * @see HdmiControlManager#removeHotplugEventListener(HotplugEventListener)
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void addHotplugEventListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull HotplugEventListener listener) {
         if (mService == null) {
             Log.e(TAG, "HdmiControlService is not available");
             return;
@@ -907,7 +931,8 @@
             Log.e(TAG, "listener is already registered");
             return;
         }
-        IHdmiHotplugEventListener wrappedListener = getHotplugEventListenerWrapper(listener);
+        IHdmiHotplugEventListener wrappedListener =
+                getHotplugEventListenerWrapper(executor, listener);
         mHotplugEventListeners.put(listener, wrappedListener);
         try {
             mService.addHotplugEventListener(wrappedListener);
@@ -943,11 +968,12 @@
     }
 
     private IHdmiHotplugEventListener getHotplugEventListenerWrapper(
-            final HotplugEventListener listener) {
+            Executor executor, final HotplugEventListener listener) {
         return new IHdmiHotplugEventListener.Stub() {
             @Override
             public void onReceived(HdmiHotplugEvent event) {
-                listener.onReceived(event);;
+                Binder.clearCallingIdentity();
+                executor.execute(() -> listener.onReceived(event));
             }
         };
     }
@@ -958,6 +984,28 @@
      * <p>To stop getting the notification,
      * use {@link #removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener)}.
      *
+     * Note that each invocation of the callback will be executed on an arbitrary
+     * Binder thread. This means that all callback implementations must be
+     * thread safe. To specify the execution thread, use
+     * {@link addHdmiControlStatusChangeListener(Executor, HdmiControlStatusChangeListener)}.
+     *
+     * @param listener {@link HdmiControlStatusChangeListener} instance
+     * @see HdmiControlManager#removeHdmiControlStatusChangeListener(
+     * HdmiControlStatusChangeListener)
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.HDMI_CEC)
+    public void addHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) {
+        addHdmiControlStatusChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
+    }
+
+    /**
+     * Adds a listener to get informed of {@link HdmiControlStatusChange}.
+     *
+     * <p>To stop getting the notification,
+     * use {@link #removeHdmiControlStatusChangeListener(HdmiControlStatusChangeListener)}.
+     *
      * @param listener {@link HdmiControlStatusChangeListener} instance
      * @see HdmiControlManager#removeHdmiControlStatusChangeListener(
      * HdmiControlStatusChangeListener)
@@ -965,7 +1013,8 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.HDMI_CEC)
-    public void addHdmiControlStatusChangeListener(HdmiControlStatusChangeListener listener) {
+    public void addHdmiControlStatusChangeListener(@NonNull @CallbackExecutor Executor executor,
+            @NonNull HdmiControlStatusChangeListener listener) {
         if (mService == null) {
             Log.e(TAG, "HdmiControlService is not available");
             return;
@@ -975,7 +1024,7 @@
             return;
         }
         IHdmiControlStatusChangeListener wrappedListener =
-                getHdmiControlStatusChangeListenerWrapper(listener);
+                getHdmiControlStatusChangeListenerWrapper(executor, listener);
         mHdmiControlStatusChangeListeners.put(listener, wrappedListener);
         try {
             mService.addHdmiControlStatusChangeListener(wrappedListener);
@@ -1011,11 +1060,12 @@
     }
 
     private IHdmiControlStatusChangeListener getHdmiControlStatusChangeListenerWrapper(
-            final HdmiControlStatusChangeListener listener) {
+            Executor executor, final HdmiControlStatusChangeListener listener) {
         return new IHdmiControlStatusChangeListener.Stub() {
             @Override
             public void onStatusChange(boolean isCecEnabled, boolean isCecAvailable) {
-                listener.onStatusChange(isCecEnabled, isCecAvailable);
+                Binder.clearCallingIdentity();
+                executor.execute(() -> listener.onStatusChange(isCecEnabled, isCecAvailable));
             }
         };
     }
diff --git a/core/java/android/net/ITetheredInterfaceCallback.aidl b/core/java/android/net/ITetheredInterfaceCallback.aidl
index e3d0759..14aa023 100644
--- a/core/java/android/net/ITetheredInterfaceCallback.aidl
+++ b/core/java/android/net/ITetheredInterfaceCallback.aidl
@@ -17,7 +17,7 @@
 package android.net;
 
 /** @hide */
-interface ITetheredInterfaceCallback {
+oneway interface ITetheredInterfaceCallback {
     void onAvailable(in String iface);
     void onUnavailable();
 }
\ No newline at end of file
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 9f592e8..4b2cfe2 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1115,8 +1115,6 @@
             if (outStream != null)
                 outStream.close();
         }
-
-        VMDebug.startEmulatorTracing();
     }
 
     /**
@@ -1130,8 +1128,6 @@
      * region of code.</p>
      */
     public static void stopNativeTracing() {
-        VMDebug.stopEmulatorTracing();
-
         // Open the sysfs file for writing and write "0" to it.
         PrintWriter outStream = null;
         try {
@@ -1160,7 +1156,7 @@
      * To temporarily enable tracing, use {@link #startNativeTracing()}.
      */
     public static void enableEmulatorTraceOutput() {
-        VMDebug.startEmulatorTracing();
+        Log.w(TAG, "Unimplemented");
     }
 
     /**
@@ -2097,25 +2093,6 @@
     public static final native int getBinderDeathObjectCount();
 
     /**
-     * Primes the register map cache.
-     *
-     * Only works for classes in the bootstrap class loader.  Does not
-     * cause classes to be loaded if they're not already present.
-     *
-     * The classAndMethodDesc argument is a concatentation of the VM-internal
-     * class descriptor, method name, and method descriptor.  Examples:
-     *     Landroid/os/Looper;.loop:()V
-     *     Landroid/app/ActivityThread;.main:([Ljava/lang/String;)V
-     *
-     * @param classAndMethodDesc the method to prepare
-     *
-     * @hide
-     */
-    public static final boolean cacheRegisterMap(String classAndMethodDesc) {
-        return VMDebug.cacheRegisterMap(classAndMethodDesc);
-    }
-
-    /**
      * Dumps the contents of VM reference tables (e.g. JNI locals and
      * globals) to the log file.
      *
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 63ef186..415e5a6 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -269,8 +269,6 @@
     private static final int EX_UNSUPPORTED_OPERATION = -7;
     private static final int EX_SERVICE_SPECIFIC = -8;
     private static final int EX_PARCELABLE = -9;
-    private static final int EX_TIMEOUT = -10;
-
     /** @hide */
     public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127; // special; see below
     private static final int EX_HAS_STRICTMODE_REPLY_HEADER = -128;  // special; see below
@@ -2158,7 +2156,6 @@
      * <li>{@link SecurityException}
      * <li>{@link UnsupportedOperationException}
      * <li>{@link NetworkOnMainThreadException}
-     * <li>{@link AndroidTimeoutException}
      * </ul>
      *
      * @param e The Exception to be written.
@@ -2228,8 +2225,6 @@
             code = EX_UNSUPPORTED_OPERATION;
         } else if (e instanceof ServiceSpecificException) {
             code = EX_SERVICE_SPECIFIC;
-        } else if (e instanceof AndroidTimeoutException) {
-            code = EX_TIMEOUT;
         }
         return code;
     }
@@ -2408,8 +2403,6 @@
                 return new UnsupportedOperationException(msg);
             case EX_SERVICE_SPECIFIC:
                 return new ServiceSpecificException(readInt(), msg);
-            case EX_TIMEOUT:
-                return new AndroidTimeoutException(msg);
             default:
                 return null;
         }
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index 8fd303b..44d1402 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -40,9 +40,9 @@
     public static final class TimeZoneMapping {
 
         @NonNull
-        private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
+        private com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
 
-        TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) {
+        TimeZoneMapping(com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping delegate) {
             this.mDelegate = Objects.requireNonNull(delegate);
         }
 
@@ -147,9 +147,9 @@
     }
 
     @NonNull
-    private final libcore.timezone.CountryTimeZones mDelegate;
+    private final com.android.i18n.timezone.CountryTimeZones mDelegate;
 
-    CountryTimeZones(libcore.timezone.CountryTimeZones delegate) {
+    CountryTimeZones(com.android.i18n.timezone.CountryTimeZones delegate) {
         mDelegate = delegate;
     }
 
@@ -221,7 +221,7 @@
     @Nullable
     public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias,
             int totalOffsetMillis, boolean isDst) {
-        libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
+        com.android.i18n.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
                 mDelegate.lookupByOffsetWithBias(
                         whenMillis, bias, totalOffsetMillis, isDst);
         return delegateOffsetResult == null ? null :
@@ -244,7 +244,7 @@
     @Nullable
     public OffsetResult lookupByOffsetWithBias(long whenMillis, @Nullable TimeZone bias,
             int totalOffsetMillis) {
-        libcore.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
+        com.android.i18n.timezone.CountryTimeZones.OffsetResult delegateOffsetResult =
                 mDelegate.lookupByOffsetWithBias(whenMillis, bias, totalOffsetMillis);
         return delegateOffsetResult == null ? null :
                 new OffsetResult(
@@ -260,11 +260,12 @@
      */
     @NonNull
     public List<TimeZoneMapping> getEffectiveTimeZoneMappingsAt(long whenMillis) {
-        List<libcore.timezone.CountryTimeZones.TimeZoneMapping> delegateList =
+        List<com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping> delegateList =
                 mDelegate.getEffectiveTimeZoneMappingsAt(whenMillis);
 
         List<TimeZoneMapping> toReturn = new ArrayList<>(delegateList.size());
-        for (libcore.timezone.CountryTimeZones.TimeZoneMapping delegateMapping : delegateList) {
+        for (com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping delegateMapping
+                : delegateList) {
             toReturn.add(new TimeZoneMapping(delegateMapping));
         }
         return Collections.unmodifiableList(toReturn);
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
index a4c3fbd..c97bf28 100644
--- a/core/java/android/timezone/TelephonyLookup.java
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -41,16 +41,17 @@
     public static TelephonyLookup getInstance() {
         synchronized (sLock) {
             if (sInstance == null) {
-                sInstance = new TelephonyLookup(libcore.timezone.TelephonyLookup.getInstance());
+                sInstance = new TelephonyLookup(com.android.i18n.timezone.TelephonyLookup
+                    .getInstance());
             }
             return sInstance;
         }
     }
 
     @NonNull
-    private final libcore.timezone.TelephonyLookup mDelegate;
+    private final com.android.i18n.timezone.TelephonyLookup mDelegate;
 
-    private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) {
+    private TelephonyLookup(@NonNull com.android.i18n.timezone.TelephonyLookup delegate) {
         mDelegate = Objects.requireNonNull(delegate);
     }
 
@@ -60,7 +61,7 @@
      */
     @Nullable
     public TelephonyNetworkFinder getTelephonyNetworkFinder() {
-        libcore.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate =
+        com.android.i18n.timezone.TelephonyNetworkFinder telephonyNetworkFinderDelegate =
                 mDelegate.getTelephonyNetworkFinder();
         return telephonyNetworkFinderDelegate != null
                 ? new TelephonyNetworkFinder(telephonyNetworkFinderDelegate) : null;
diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java
index 823cd25..3b65c6f 100644
--- a/core/java/android/timezone/TelephonyNetwork.java
+++ b/core/java/android/timezone/TelephonyNetwork.java
@@ -28,9 +28,9 @@
 public final class TelephonyNetwork {
 
     @NonNull
-    private final libcore.timezone.TelephonyNetwork mDelegate;
+    private final com.android.i18n.timezone.TelephonyNetwork mDelegate;
 
-    TelephonyNetwork(@NonNull libcore.timezone.TelephonyNetwork delegate) {
+    TelephonyNetwork(@NonNull com.android.i18n.timezone.TelephonyNetwork delegate) {
         mDelegate = Objects.requireNonNull(delegate);
     }
 
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index 4bfeff8..c69ddf8 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -29,9 +29,9 @@
 public final class TelephonyNetworkFinder {
 
     @NonNull
-    private final libcore.timezone.TelephonyNetworkFinder mDelegate;
+    private final com.android.i18n.timezone.TelephonyNetworkFinder mDelegate;
 
-    TelephonyNetworkFinder(libcore.timezone.TelephonyNetworkFinder delegate) {
+    TelephonyNetworkFinder(com.android.i18n.timezone.TelephonyNetworkFinder delegate) {
         mDelegate = Objects.requireNonNull(delegate);
     }
 
@@ -45,7 +45,7 @@
         Objects.requireNonNull(mcc);
         Objects.requireNonNull(mnc);
 
-        libcore.timezone.TelephonyNetwork telephonyNetworkDelegate =
+        com.android.i18n.timezone.TelephonyNetwork telephonyNetworkDelegate =
                 mDelegate.findNetworkByMccMnc(mcc, mnc);
         return telephonyNetworkDelegate != null
                 ? new TelephonyNetwork(telephonyNetworkDelegate) : null;
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
index 03f5013..bf4275f 100644
--- a/core/java/android/timezone/TimeZoneFinder.java
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -41,16 +41,17 @@
     public static TimeZoneFinder getInstance() {
         synchronized (sLock) {
             if (sInstance == null) {
-                sInstance = new TimeZoneFinder(libcore.timezone.TimeZoneFinder.getInstance());
+                sInstance = new TimeZoneFinder(com.android.i18n.timezone.TimeZoneFinder
+                    .getInstance());
             }
         }
         return sInstance;
     }
 
     @NonNull
-    private final libcore.timezone.TimeZoneFinder mDelegate;
+    private final com.android.i18n.timezone.TimeZoneFinder mDelegate;
 
-    private TimeZoneFinder(@NonNull libcore.timezone.TimeZoneFinder delegate) {
+    private TimeZoneFinder(@NonNull com.android.i18n.timezone.TimeZoneFinder delegate) {
         mDelegate = Objects.requireNonNull(delegate);
     }
 
@@ -70,7 +71,8 @@
      */
     @Nullable
     public CountryTimeZones lookupCountryTimeZones(@NonNull String countryIso) {
-        libcore.timezone.CountryTimeZones delegate = mDelegate.lookupCountryTimeZones(countryIso);
+        com.android.i18n.timezone.CountryTimeZones delegate = mDelegate
+                .lookupCountryTimeZones(countryIso);
         return delegate == null ? null : new CountryTimeZones(delegate);
     }
 }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 8f2ead3..e0b8d52 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -23,12 +23,11 @@
 import android.os.Build;
 import android.os.SystemClock;
 
+import com.android.i18n.timezone.CountryTimeZones;
+import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping;
+import com.android.i18n.timezone.TimeZoneFinder;
 import com.android.i18n.timezone.ZoneInfoDb;
 
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.TimeZoneFinder;
-
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.time.LocalTime;
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 0d2d4d1..ffeeb80 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -500,12 +500,13 @@
      */
     public static ViewConfiguration get(Context context) {
         if (!context.isUiContext() && vmIncorrectContextUseEnabled()) {
-            final String errorMessage = "Tried to access UI constants from a non-visual Context.";
+            final String errorMessage = "Tried to access UI constants from a non-visual Context:"
+                    + context;
             final String message = "UI constants, such as display metrics or window metrics, "
                     + "must be accessed from Activity or other visual Context. "
                     + "Use an Activity or a Context created with "
                     + "Context#createWindowContext(int, Bundle), which are adjusted to the "
-                    + "configuration and visual bounds of an area on screen.";
+                    + "configuration and visual bounds of an area on screen";
             final Exception exception = new IllegalArgumentException(errorMessage);
             StrictMode.onIncorrectContextUsed(message, exception);
             Log.e(TAG, errorMessage + message, exception);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 2fac35f..795e8f3 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1444,11 +1444,22 @@
         @Deprecated
         public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
 
-        /** Window flag: When set, input method can't interact with the focusable window
-         * and can be placed to use more space and cover the input method.
-         * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no
-         * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE}
-         * flag set.
+        /** Window flag: when set, inverts the input method focusability of the window.
+         *
+         * The effect of setting this flag depends on whether {@link #FLAG_NOT_FOCUSABLE} is set:
+         * <p>
+         * If {@link #FLAG_NOT_FOCUSABLE} is <em>not</em> set, i.e. when the window is focusable,
+         * setting this flag prevents this window from becoming the target of the input method.
+         * Consequently, it will <em>not</em> be able to interact with the input method,
+         * and will be layered above the input method (unless there is another input method
+         * target above it).
+         *
+         * <p>
+         * If {@link #FLAG_NOT_FOCUSABLE} <em>is</em> set, setting this flag requests for the window
+         * to be the input method target even though  the window is <em>not</em> focusable.
+         * Consequently, it will be layered below the input method.
+         * Note: Windows that set {@link #FLAG_NOT_FOCUSABLE} cannot interact with the input method,
+         * regardless of this flag.
          */
         public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
 
@@ -2141,13 +2152,12 @@
          * focus.  In particular, this checks the
          * {@link #FLAG_NOT_FOCUSABLE} and {@link #FLAG_ALT_FOCUSABLE_IM}
          * flags and returns true if the combination of the two corresponds
-         * to a window that needs to be behind the input method so that the
-         * user can type into it.
+         * to a window that can use the input method.
          *
          * @param flags The current window manager flags.
          *
-         * @return Returns {@code true} if such a window should be behind/interact
-         * with an input method, {@code false} if not.
+         * @return Returns {@code true} if a window with the given flags would be able to
+         * use the input method, {@code false} if not.
          */
         public static boolean mayUseInputMethod(int flags) {
             return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
new file mode 100644
index 0000000..e78e32b
--- /dev/null
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.listeners;
+
+import android.annotation.Nullable;
+
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
+
+/**
+ * Interface (trait) for executing listener style operations on an executor.
+ */
+public interface ListenerExecutor {
+
+    /**
+     * An listener operation to perform.
+     *
+     * @param <TListener> listener type
+     */
+    interface ListenerOperation<TListener> {
+        /**
+         * Performs the operation on the given listener.
+         */
+        void operate(TListener listener) throws Exception;
+
+        /**
+         * Called before this operation is to be run. Some operations may be canceled before they
+         * are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
+         * will only be run if this method was run.
+         */
+        default void onPreExecute() {}
+
+        /**
+         * Called if the operation fails while running. Will not be invoked in the event of a
+         * RuntimeException, which will propagate normally. Implementations of
+         * {@link ListenerExecutor} have the option to override
+         * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
+         * intercept failures at the class level.
+         */
+        default void onFailure(Exception e) {
+            // implementations should handle any exceptions that may be thrown
+            throw new AssertionError(e);
+        }
+
+        /**
+         * Called after the operation is run. This method will always be called if
+         * {@link #onPreExecute()} is called. Success implies that the operation was run to
+         * completion with no failures.
+         */
+        default void onPostExecute(boolean success) {}
+
+        /**
+         * Called after this operation is complete (which does not imply that it was necessarily
+         * run). Will always be called once per operation, no matter if the operation was run or
+         * not. Success implies that the operation was run to completion with no failures.
+         */
+        default void onComplete(boolean success) {}
+    }
+
+    /**
+     * May be override to handle operation failures at a class level. Will not be invoked in the
+     * event of a RuntimeException, which will propagate normally.
+     */
+    default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
+            Exception exception) {
+        operation.onFailure(exception);
+    }
+
+    /**
+     * Executes the given listener operation on the given executor, using the provided listener
+     * supplier. If the supplier returns a null value, or a value during the operation that does not
+     * match the value prior to the operation, then the operation is considered canceled.
+     */
+    default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
+            @Nullable ListenerOperation<TListener> operation) {
+        if (operation == null) {
+            return;
+        }
+
+        boolean executing = false;
+        boolean preexecute = false;
+        try {
+            TListener listener = listenerSupplier.get();
+            if (listener == null) {
+                return;
+            }
+
+            operation.onPreExecute();
+            preexecute = true;
+            executor.execute(() -> {
+                boolean success = false;
+                try {
+                    if (listener == listenerSupplier.get()) {
+                        operation.operate(listener);
+                        success = true;
+                    }
+                } catch (Exception e) {
+                    if (e instanceof RuntimeException) {
+                        throw (RuntimeException) e;
+                    } else {
+                        onOperationFailure(operation, e);
+                    }
+                } finally {
+                    operation.onPostExecute(success);
+                    operation.onComplete(success);
+                }
+            });
+            executing = true;
+        } finally {
+            if (!executing) {
+                if (preexecute) {
+                    operation.onPostExecute(false);
+                }
+                operation.onComplete(false);
+            }
+        }
+    }
+}
diff --git a/core/java/com/android/internal/listeners/ListenerTransport.java b/core/java/com/android/internal/listeners/ListenerTransport.java
index 2a52179..9d6210e 100644
--- a/core/java/com/android/internal/listeners/ListenerTransport.java
+++ b/core/java/com/android/internal/listeners/ListenerTransport.java
@@ -45,10 +45,6 @@
         mListener = listener;
     }
 
-    final boolean isRegistered() {
-        return mListener != null;
-    }
-
     /**
      * Prevents any listener invocations that happen-after this call.
      */
diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java
deleted file mode 100644
index daef88a..0000000
--- a/core/java/com/android/internal/listeners/ListenerTransportManager.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.listeners;
-
-import android.annotation.NonNull;
-import android.os.RemoteException;
-import android.util.ArrayMap;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.util.Objects;
-
-/**
- * A listener manager which tracks listeners along with their keys. This class enforces proper use
- * of transport objects and ensure unregistration race conditions are handled properly. If listeners
- * should be multiplexed before being sent to the server, see {@link ListenerTransportMultiplexer}
- * instead.
- *
- * @param <TTransport> transport type
- */
-public abstract class ListenerTransportManager<TTransport extends ListenerTransport<?>> {
-
-    @GuardedBy("mTransports")
-    private final ArrayMap<Object, TTransport> mTransports = new ArrayMap<>();
-
-    /**
-     * Should be implemented to register the transport with the server.
-     *
-     * @see #reregisterWithServer(ListenerTransport, ListenerTransport)
-     */
-    protected abstract void registerWithServer(TTransport transport) throws RemoteException;
-
-    /**
-     * Invoked when the server already has a transport registered for a key, and it is being
-     * replaced with a new transport. The default implementation unregisters the old transport, then
-     * registers the new transport, but this may be overridden by subclasses in order to reregister
-     * more efficiently.
-     */
-    protected void reregisterWithServer(TTransport oldTransport, TTransport newTransport)
-            throws RemoteException {
-        unregisterWithServer(oldTransport);
-        registerWithServer(newTransport);
-    }
-
-    /**
-     * Should be implemented to unregister the transport from the server.
-     */
-    protected abstract void unregisterWithServer(TTransport transport) throws RemoteException;
-
-    /**
-     * Adds a new transport with the given key and makes a call to add the transport server side. If
-     * a transport already exists with that key, it will be replaced by the new transport and
-     * {@link #reregisterWithServer(ListenerTransport, ListenerTransport)} will be invoked to
-     * replace the old transport with the new transport server side. If no transport exists with
-     * that key, it will be added server side via {@link #registerWithServer(ListenerTransport)}.
-     */
-    protected void registerListener(@NonNull Object key, @NonNull TTransport transport) {
-        Objects.requireNonNull(key);
-        Objects.requireNonNull(transport);
-
-        synchronized (mTransports) {
-            TTransport oldTransport = mTransports.put(key, transport);
-            if (oldTransport != null) {
-                oldTransport.unregister();
-            }
-
-            Preconditions.checkState(transport.isRegistered());
-
-            boolean registered = false;
-            try {
-                if (oldTransport == null) {
-                    registerWithServer(transport);
-                } else {
-                    reregisterWithServer(oldTransport, transport);
-                }
-                registered = true;
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            } finally {
-                if (!registered) {
-                    transport.unregister();
-                    mTransports.remove(key);
-                }
-            }
-        }
-    }
-
-    /**
-     * Removes the transport with the given key, and makes a call to remove the transport server
-     * side via {@link #unregisterWithServer(ListenerTransport)}.
-     */
-    protected void unregisterListener(@NonNull Object key) {
-        Objects.requireNonNull(key);
-
-        synchronized (mTransports) {
-            TTransport transport = mTransports.remove(key);
-            if (transport == null) {
-                return;
-            }
-
-            transport.unregister();
-            try {
-                unregisterWithServer(transport);
-            } catch (RemoteException e) {
-                throw e.rethrowFromSystemServer();
-            }
-        }
-    }
-
-    /**
-     * Removes the given transport with the given key if such a mapping exists. This only removes
-     * the client registration, it does not make any calls to remove the transport server side. The
-     * intended use is for when the transport is already removed server side and only client side
-     * cleanup is necessary.
-     */
-    protected void removeTransport(@NonNull Object key, @NonNull ListenerTransport<?> transport) {
-        Objects.requireNonNull(key);
-        Objects.requireNonNull(transport);
-
-        synchronized (mTransports) {
-            TTransport typedTransport = mTransports.get(key);
-            if (typedTransport != transport) {
-                return;
-            }
-
-            mTransports.remove(key);
-            typedTransport.unregister();
-        }
-    }
-}
diff --git a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
index 1721a3e..fc1d69f 100644
--- a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
+++ b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java
@@ -21,13 +21,12 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Objects;
@@ -38,8 +37,7 @@
  * A listener multiplexer designed for use by client-side code. This class ensures that listeners
  * are never invoked while a lock is held. This class is only useful for multiplexing listeners -
  * if all client listeners can be combined into a single server request, and all server results will
- * be delivered to all clients. If this is not the case, see {@link ListenerTransportManager}
- * instead.
+ * be delivered to all clients.
  *
  * By default, the multiplexer will replace requests on the server simply by registering the new
  * request and trusting the server to know this is replacing the old request. If the server needs to
@@ -229,9 +227,7 @@
     /**
      * Dumps debug information.
      */
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-
+    public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
         ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> registrations;
         synchronized (mLock) {
             registrations = mRegistrations;
@@ -239,12 +235,12 @@
             ipw.print("service: ");
             if (mServiceRegistered) {
                 if (mCurrentRequest == null) {
-                    pw.print("request registered");
+                    ipw.print("request registered");
                 } else {
-                    pw.print("request registered - " + mCurrentRequest);
+                    ipw.print("request registered - " + mCurrentRequest);
                 }
             } else {
-                pw.print("unregistered");
+                ipw.print("unregistered");
             }
             ipw.println();
         }
diff --git a/core/java/com/android/internal/logging/AndroidHandler.java b/core/java/com/android/internal/logging/AndroidHandler.java
index f55a31f..119f366 100644
--- a/core/java/com/android/internal/logging/AndroidHandler.java
+++ b/core/java/com/android/internal/logging/AndroidHandler.java
@@ -17,9 +17,8 @@
 package com.android.internal.logging;
 
 import android.util.Log;
+
 import com.android.internal.util.FastPrintWriter;
-import dalvik.system.DalvikLogging;
-import dalvik.system.DalvikLogHandler;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -82,7 +81,7 @@
  *   </tr>
  * </table>
  */
-public class AndroidHandler extends Handler implements DalvikLogHandler {
+public class AndroidHandler extends Handler {
     /**
      * Holds the formatter for all Android log handlers.
      */
@@ -121,10 +120,32 @@
         // No need to flush, but must implement abstract method.
     }
 
+    /**
+     * Returns the short logger tag (up to 23 chars) for the given logger name.
+     * Traditionally loggers are named by fully-qualified Java classes; this
+     * method attempts to return a concise identifying part of such names.
+     */
+    private static String loggerNameToTag(String loggerName) {
+        // Anonymous logger.
+        if (loggerName == null) {
+            return "null";
+        }
+
+        int length = loggerName.length();
+        if (length <= 23) {
+            return loggerName;
+        }
+
+        int lastPeriod = loggerName.lastIndexOf(".");
+        return length - (lastPeriod + 1) <= 23
+                ? loggerName.substring(lastPeriod + 1)
+                : loggerName.substring(loggerName.length() - 23);
+    }
+
     @Override
     public void publish(LogRecord record) {
         int level = getAndroidLevel(record.getLevel());
-        String tag = DalvikLogging.loggerNameToTag(record.getLoggerName());
+        String tag = loggerNameToTag(record.getLoggerName());
         if (!Log.isLoggable(tag, level)) {
             return;
         }
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 884d2c7e..02213ed 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -309,7 +309,7 @@
     repeated .android.graphics.RectProto frozen_bounds = 23;
     optional bool visible = 24;
     reserved 25; // configuration_container
-    optional IdentifierProto identifier = 26;
+    optional IdentifierProto identifier = 26 [deprecated=true];
     optional string state = 27 [(.android.privacy).dest = DEST_EXPLICIT];
     optional bool front_of_task = 28;
     optional int32 proc_id = 29;
@@ -332,7 +332,7 @@
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional WindowContainerProto window_container = 1;
-    optional IdentifierProto identifier = 2;
+    optional IdentifierProto identifier = 2 [deprecated=true];
     // Unique identifier of a DisplayContent stack.
     optional int32 display_id = 3;
     // Unique identifier for the task stack.
@@ -428,6 +428,7 @@
     optional bool visible = 3;
     optional SurfaceAnimatorProto surface_animator = 4;
     repeated WindowContainerChildProto children = 5;
+    optional IdentifierProto identifier = 6;
 }
 
 /* represents a generic child of a WindowContainer */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e87b82a..16797fd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5068,7 +5068,7 @@
                 android:process=":ui"
                 android:exported="true"
                 android:visibleToInstantApps="true">
-            <intent-filter>
+            <intent-filter android:priority="100">
                 <action android:name="android.intent.action.CHOOSER" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.VOICE" />
diff --git a/core/res/res/layout/notification_material_action_list.xml b/core/res/res/layout/notification_material_action_list.xml
index 3615b9e..df271f0 100644
--- a/core/res/res/layout/notification_material_action_list.xml
+++ b/core/res/res/layout/notification_material_action_list.xml
@@ -25,6 +25,7 @@
             android:id="@+id/actions_container_layout"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
+            android:gravity="end"
             android:orientation="horizontal"
             android:paddingEnd="@dimen/bubble_gone_padding_end"
             >
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index c72707db..1533377 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -58,6 +58,8 @@
     private Handler mHandler;
     private Executor mExecutor;
     private BugreportManager mBrm;
+    private File mBugreportFile;
+    private File mScreenshotFile;
     private ParcelFileDescriptor mBugreportFd;
     private ParcelFileDescriptor mScreenshotFd;
 
@@ -73,8 +75,10 @@
         };
 
         mBrm = getBugreportManager();
-        mBugreportFd = parcelFd("bugreport_" + name.getMethodName(), ".zip");
-        mScreenshotFd = parcelFd("screenshot_" + name.getMethodName(), ".png");
+        mBugreportFile = createTempFile("bugreport_" + name.getMethodName(), ".zip");
+        mScreenshotFile = createTempFile("screenshot_" + name.getMethodName(), ".png");
+        mBugreportFd = parcelFd(mBugreportFile);
+        mScreenshotFd = parcelFd(mScreenshotFile);
 
         getPermissions();
     }
@@ -121,6 +125,21 @@
     }
 
     @Test
+    public void normalFlow_full() throws Exception {
+        BugreportCallbackImpl callback = new BugreportCallbackImpl();
+        mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
+
+        waitTillDoneOrTimeout(callback);
+        assertThat(callback.isDone()).isTrue();
+        assertThat(callback.getErrorCode()).isEqualTo(
+                BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+        // bugreport and screenshot files should be empty when user consent timed out.
+        assertThat(mBugreportFile.length()).isEqualTo(0);
+        assertThat(mScreenshotFile.length()).isEqualTo(0);
+        assertFdsAreClosed(mBugreportFd, mScreenshotFd);
+    }
+
+    @Test
     public void simultaneousBugreportsNotAllowed() throws Exception {
         // Start bugreport #1
         BugreportCallbackImpl callback = new BugreportCallbackImpl();
@@ -129,9 +148,10 @@
         // Before #1 is done, try to start #2.
         assertThat(callback.isDone()).isFalse();
         BugreportCallbackImpl callback2 = new BugreportCallbackImpl();
-        ParcelFileDescriptor bugreportFd2 = parcelFd("bugreport_2_" + name.getMethodName(), ".zip");
-        ParcelFileDescriptor screenshotFd2 =
-                parcelFd("screenshot_2_" + name.getMethodName(), ".png");
+        File bugreportFile2 = createTempFile("bugreport_2_" + name.getMethodName(), ".zip");
+        File screenshotFile2 = createTempFile("screenshot_2_" + name.getMethodName(), ".png");
+        ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
+        ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2);
         mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
         Thread.sleep(500 /* .5s */);
 
@@ -271,12 +291,16 @@
         return bm;
     }
 
-    private static ParcelFileDescriptor parcelFd(String prefix, String extension) throws Exception {
-        File f = File.createTempFile(prefix, extension);
+    private static File createTempFile(String prefix, String extension) throws Exception {
+        final File f = File.createTempFile(prefix, extension);
         f.setReadable(true, true);
         f.setWritable(true, true);
+        f.deleteOnExit();
+        return f;
+    }
 
-        return ParcelFileDescriptor.open(f,
+    private static ParcelFileDescriptor parcelFd(File file) throws Exception {
+        return ParcelFileDescriptor.open(file,
                 ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_APPEND);
     }
 
@@ -342,4 +366,13 @@
     private static BugreportParams interactive() {
         return new BugreportParams(BugreportParams.BUGREPORT_MODE_INTERACTIVE);
     }
+
+    /*
+     * Returns a {@link BugreportParams} for full bugreport that includes a screenshot.
+     *
+     * <p> This can take on the order of minutes to finish
+     */
+    private static BugreportParams full() {
+        return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
+    }
 }
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 46885b4..3c5d951 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1511,12 +1511,6 @@
             android:process=":SlowProvider">
         </provider>
 
-        <provider
-            android:name="android.content.TimingOutProvider"
-            android:authorities="android.content.TimingOutProvider"
-            android:process=":TimingOutProvider">
-        </provider>
-
         <!-- Application components used for os tests -->
 
         <service android:name="android.os.MessengerService"
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 43cfa17..f48e666 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -32,7 +32,6 @@
 import android.graphics.ImageDecoder;
 import android.graphics.Paint;
 import android.net.Uri;
-import android.os.AndroidTimeoutException;
 import android.os.MemoryFile;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
@@ -227,20 +226,6 @@
     }
 
     @Test
-    public void testCall_timingOutProvider() {
-        try {
-            // This provider is running in a different process and is configured to time out
-            // on start. We acquire it as "unstable" to avoid getting killed by the timeout in the
-            // content provider process.
-            mResolver.acquireUnstableContentProviderClient(
-                    Uri.parse("content://android.content.TimingOutProvider"));
-            fail("AndroidTimeoutException expected");
-        } catch (AndroidTimeoutException t) {
-            assertThat(t).hasMessageThat().contains("android.content.TimingOutProvider");
-        }
-    }
-
-    @Test
     public void testGetType_unknownProvider() {
         // This provider does not exist.
         // We are trying to confirm that getType returns null and does not cause an ANR
diff --git a/core/tests/coretests/src/android/content/TimingOutProvider.java b/core/tests/coretests/src/android/content/TimingOutProvider.java
deleted file mode 100644
index 0651e24..0000000
--- a/core/tests/coretests/src/android/content/TimingOutProvider.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * A dummy content provider for tests.  This provider is marked with a "simulatedTimout"
- * meta=data parameter, which causes ActivityManager to treat this provider as timing out.
- */
-public class TimingOutProvider extends ContentProvider {
-
-    @Override
-    public boolean onCreate() {
-        try {
-            // Sleep for longer than ContentResolver#CONTENT_PROVIDER_READY_TIMEOUT_MILLIS
-            Thread.sleep(25000);
-        } catch (InterruptedException e) {
-            // Ignore
-        }
-        return true;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return "slow";
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 6c07914..73296987 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -757,6 +757,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-547111355": {
+      "message": "hideIme Control target: %s ",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_IME",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "-545190927": {
       "message": "<<< CLOSE TRANSACTION animate",
       "level": "INFO",
@@ -1093,6 +1099,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "95216706": {
+      "message": "hideIme target: %s ",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_IME",
+      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    },
     "95281111": {
       "message": "Attempted to get IME flag of a display that does not exist: %d",
       "level": "WARN",
diff --git a/data/keyboards/Vendor_1532_Product_0705.kl b/data/keyboards/Vendor_1532_Product_0705.kl
index e5afe39..611aaec 100644
--- a/data/keyboards/Vendor_1532_Product_0705.kl
+++ b/data/keyboards/Vendor_1532_Product_0705.kl
@@ -32,12 +32,6 @@
 key 0x138    BUTTON_L2
 key 0x139    BUTTON_R2
 
-# L2 axis
-axis 0x03   LTRIGGER
-# R2 axis
-axis 0x04   RTRIGGER
-
-
 # Left Analog Stick
 axis 0x00    X
 axis 0x01    Y
@@ -45,6 +39,11 @@
 axis 0x02    Z
 axis 0x05    RZ
 
+# L2 axis
+axis 0x09    RTRIGGER
+# R2 axis
+axis 0x0a    LTRIGGER
+
 # Left stick click
 key 0x13d    BUTTON_THUMBL
 # Right stick click
diff --git a/data/keyboards/Vendor_1532_Product_0707.kl b/data/keyboards/Vendor_1532_Product_0707.kl
index dec52d93..48c1714 100644
--- a/data/keyboards/Vendor_1532_Product_0707.kl
+++ b/data/keyboards/Vendor_1532_Product_0707.kl
@@ -32,12 +32,6 @@
 key 0x138    BUTTON_L2
 key 0x139    BUTTON_R2
 
-# L2 axis
-axis 0x03   LTRIGGER
-# R2 axis
-axis 0x04   RTRIGGER
-
-
 # Left Analog Stick
 axis 0x00    X
 axis 0x01    Y
@@ -45,6 +39,11 @@
 axis 0x02    Z
 axis 0x05    RZ
 
+# L2 axis
+axis 0x09    RTRIGGER
+# R2 axis
+axis 0x0a    LTRIGGER
+
 # Left stick click
 key 0x13d    BUTTON_THUMBL
 # Right stick click
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index d4cf53e..6ad8d2c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -31,7 +31,6 @@
 import com.android.org.bouncycastle.asn1.ASN1Integer;
 import com.android.org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import com.android.org.bouncycastle.asn1.DERBitString;
-import com.android.org.bouncycastle.asn1.DERInteger;
 import com.android.org.bouncycastle.asn1.DERNull;
 import com.android.org.bouncycastle.asn1.DERSequence;
 import com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -702,8 +701,8 @@
                 sigAlgOid = X9ObjectIdentifiers.ecdsa_with_SHA256;
                 sigAlgId = new AlgorithmIdentifier(sigAlgOid);
                 ASN1EncodableVector v = new ASN1EncodableVector();
-                v.add(new DERInteger(0));
-                v.add(new DERInteger(0));
+                v.add(new ASN1Integer(BigInteger.valueOf(0)));
+                v.add(new ASN1Integer(BigInteger.valueOf(0)));
                 signature = new DERSequence().getEncoded();
                 break;
             case KeymasterDefs.KM_ALGORITHM_RSA:
diff --git a/location/java/android/location/ILocationCallback.aidl b/location/java/android/location/ILocationCallback.aidl
new file mode 100644
index 0000000..deb390f
--- /dev/null
+++ b/location/java/android/location/ILocationCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.Location;
+
+/**
+ * Listener for single locations.
+ * {@hide}
+ */
+oneway interface ILocationCallback
+{
+    void onLocation(in @nullable Location location);
+}
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 8479caf..6e7f6a5 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -1,36 +1,29 @@
-/* //device/java/android/android/location/ILocationListener.aidl
-**
-** Copyright 2008, 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.
-*/
+/*
+ * Copyright (C) 2008, 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.location;
 
 import android.location.Location;
-import android.os.Bundle;
+import android.os.IRemoteCallback;
 
 /**
  * {@hide}
  */
 oneway interface ILocationListener
 {
-    @UnsupportedAppUsage
-    void onLocationChanged(in Location location);
-    @UnsupportedAppUsage
-    void onProviderEnabled(String provider);
-    @UnsupportedAppUsage
-    void onProviderDisabled(String provider);
-    // called when the listener is removed from the server side; no further callbacks are expected
-    void onRemoved();
+    void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
+    void onProviderEnabledChanged(String provider, boolean enabled);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1f7b69f..bb8f81d 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -29,6 +29,7 @@
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
 import android.location.IGnssNavigationMessageListener;
+import android.location.ILocationCallback;
 import android.location.ILocationListener;
 import android.location.Location;
 import android.location.LocationRequest;
@@ -46,9 +47,7 @@
 interface ILocationManager
 {
     Location getLastLocation(in LocationRequest request, String packageName, String attributionTag);
-    boolean getCurrentLocation(in LocationRequest request,
-            in ICancellationSignal cancellationSignal, in ILocationListener listener,
-            String packageName, String attributionTag, String listenerId);
+    void getCurrentLocation(in LocationRequest request, in ICancellationSignal cancellationSignal, in ILocationCallback callback, String packageName, String attributionTag, String listenerId);
 
     void registerLocationListener(in LocationRequest request, in ILocationListener listener, String packageName, String attributionTag, String listenerId);
     void unregisterLocationListener(in ILocationListener listener);
@@ -116,11 +115,6 @@
 
     boolean sendExtraCommand(String provider, String command, inout Bundle extras);
 
-    // --- internal ---
-
-    // for reporting callback completion
-    void locationCallbackFinished(ILocationListener listener);
-
     // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
     String[] getIgnoreSettingsWhitelist();
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 401a5a1..fed65c9 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -20,10 +20,8 @@
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.Manifest.permission.LOCATION_HARDWARE;
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
-import static android.app.AlarmManager.ELAPSED_REALTIME;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
 
 import android.Manifest;
 import android.annotation.CallbackExecutor;
@@ -34,7 +32,6 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
-import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.app.PropertyInvalidatedCache;
@@ -44,31 +41,30 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.location.util.LocationListenerTransportManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
-import android.util.ArrayMap;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor;
 import com.android.internal.listeners.ListenerTransportMultiplexer;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledRunnable;
 
+import java.lang.ref.WeakReference;
 import java.util.Collection;
 import java.util.List;
 import java.util.Objects;
+import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
-import java.util.concurrent.RejectedExecutionException;
 import java.util.function.Consumer;
 
 /**
@@ -305,11 +301,12 @@
     public static final String METADATA_SETTINGS_FOOTER_STRING =
             "com.android.settings.location.FOOTER_STRING";
 
-    private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
 
-    @GuardedBy("mProviderLocationListeners")
-    private static final ArrayMap<String, LocationListenerTransportManager>
-            sProviderLocationListeners = new ArrayMap<>();
+    private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
+
+    @GuardedBy("sLocationListeners")
+    private static final WeakHashMap<LocationListener, WeakReference<LocationListenerTransport>>
+            sLocationListeners = new WeakHashMap<>();
 
     final Context mContext;
     @UnsupportedAppUsage
@@ -643,12 +640,7 @@
     @Nullable
     @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
     public Location getLastLocation() {
-        try {
-            return mService.getLastLocation(null, mContext.getPackageName(),
-                    mContext.getAttributionTag());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return getLastKnownLocation(FUSED_PROVIDER);
     }
 
     /**
@@ -745,33 +737,19 @@
     public void getCurrentLocation(@NonNull LocationRequest locationRequest,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
-        LocationRequest currentLocationRequest = new LocationRequest(locationRequest)
-                .setNumUpdates(1);
-        if (currentLocationRequest.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
-            currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
-        }
-
-        GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor,
-                consumer);
+        ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
+        GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor, consumer,
+                remoteCancellationSignal);
 
         if (cancellationSignal != null) {
             cancellationSignal.throwIfCanceled();
+            cancellationSignal.setOnCancelListener(transport::cancel);
         }
 
-        ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
-
         try {
-            if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
+            mService.getCurrentLocation(locationRequest, remoteCancellationSignal,
                     transport, mContext.getPackageName(), mContext.getAttributionTag(),
-                    transport.getListenerId())) {
-                transport.register(mContext.getSystemService(AlarmManager.class),
-                        remoteCancellationSignal);
-                if (cancellationSignal != null) {
-                    cancellationSignal.setOnCancelListener(transport::cancel);
-                }
-            } else {
-                transport.fail();
-            }
+                    AppOpsManager.toReceiverId(consumer));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -803,7 +781,7 @@
 
         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                 provider, 0, 0, true);
-        request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+        request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
         requestLocationUpdates(request, listener, looper);
     }
 
@@ -835,7 +813,7 @@
 
         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
                 criteria, 0, 0, true);
-        request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+        request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
         requestLocationUpdates(request, listener, looper);
     }
 
@@ -862,7 +840,7 @@
 
         LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                 provider, 0, 0, true);
-        request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+        request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
         requestLocationUpdates(request, pendingIntent);
     }
 
@@ -890,7 +868,7 @@
 
         LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
                 criteria, 0, 0, true);
-        request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+        request.setExpireIn(MAX_SINGLE_LOCATION_TIMEOUT_MS);
         requestLocationUpdates(request, pendingIntent);
     }
 
@@ -1200,18 +1178,28 @@
             locationRequest = new LocationRequest();
         }
 
-        String provider = locationRequest.getProvider();
+        synchronized (sLocationListeners) {
+            WeakReference<LocationListenerTransport> reference = sLocationListeners.get(listener);
+            LocationListenerTransport transport = reference != null ? reference.get() : null;
+            if (transport == null) {
+                transport = new LocationListenerTransport(listener, executor);
+                sLocationListeners.put(listener, new WeakReference<>(transport));
+            } else {
+                transport.setExecutor(executor);
+            }
 
-        LocationListenerTransportManager manager;
-        synchronized (sProviderLocationListeners) {
-            manager = sProviderLocationListeners.get(provider);
-            if (manager == null) {
-                manager = new LocationListenerTransportManager(mService);
-                sProviderLocationListeners.put(provider, manager);
+            try {
+                // making the service call while under lock is less than ideal since LMS must
+                // make sure that callbacks are not made on the same thread - however it is the
+                // easiest way to guarantee that clients will not receive callbacks after
+                // unregistration is complete.
+                mService.registerLocationListener(locationRequest, transport,
+                        mContext.getPackageName(), mContext.getAttributionTag(),
+                        AppOpsManager.toReceiverId(listener));
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
             }
         }
-
-        manager.addListener(mContext, locationRequest, executor, listener);
     }
 
     /**
@@ -1236,13 +1224,16 @@
     public void requestLocationUpdates(
             @Nullable LocationRequest locationRequest,
             @NonNull PendingIntent pendingIntent) {
-        Preconditions.checkArgument(locationRequest != null, "invalid null location request");
         Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
         if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
             Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
                     "pending intent must be targeted to a package");
         }
 
+        if (locationRequest == null) {
+            locationRequest = new LocationRequest();
+        }
+
         try {
             mService.registerLocationPendingIntent(locationRequest, pendingIntent,
                     mContext.getPackageName(), mContext.getAttributionTag());
@@ -1294,25 +1285,24 @@
     public void removeUpdates(@NonNull LocationListener listener) {
         Preconditions.checkArgument(listener != null, "invalid null listener");
 
-        RuntimeException exception = null;
-        synchronized (sProviderLocationListeners) {
-            for (int i = 0; i < sProviderLocationListeners.size(); i++) {
-                LocationListenerTransportManager manager = sProviderLocationListeners.valueAt(i);
+        synchronized (sLocationListeners) {
+            WeakReference<LocationListenerTransport> reference = sLocationListeners.remove(
+                    listener);
+            LocationListenerTransport transport = reference != null ? reference.get() : null;
+            if (transport != null) {
+                transport.unregister();
+
                 try {
-                    manager.removeListener(listener);
-                } catch (RuntimeException e) {
-                    if (exception == null) {
-                        exception = e;
-                    } else {
-                        exception.addSuppressed(e);
-                    }
+                    // making the service call while under lock is less than ideal since LMS must
+                    // make sure that callbacks are not made on the same thread - however it is the
+                    // easiest way to guarantee that clients will not receive callbacks after
+                    // unregistration is complete.
+                    mService.unregisterLocationListener(transport);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
                 }
             }
         }
-
-        if (exception != null) {
-            throw exception;
-        }
     }
 
     /**
@@ -2397,12 +2387,10 @@
         }
     }
 
-    private static class GetCurrentLocationTransport extends ILocationListener.Stub implements
-            AlarmManager.OnAlarmListener {
+    private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements
+            ListenerExecutor {
 
-        @GuardedBy("this")
-        @Nullable
-        private Executor mExecutor;
+        private final Executor mExecutor;
 
         @GuardedBy("this")
         @Nullable
@@ -2410,61 +2398,22 @@
 
         @GuardedBy("this")
         @Nullable
-        private AlarmManager mAlarmManager;
-
-        @GuardedBy("this")
-        @Nullable
         private ICancellationSignal mRemoteCancellationSignal;
 
-        GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer) {
+        GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer,
+                ICancellationSignal remoteCancellationSignal) {
             Preconditions.checkArgument(executor != null, "illegal null executor");
             Preconditions.checkArgument(consumer != null, "illegal null consumer");
             mExecutor = executor;
             mConsumer = consumer;
-            mAlarmManager = null;
-            mRemoteCancellationSignal = null;
-        }
-
-        public String getListenerId() {
-            return AppOpsManager.toReceiverId(mConsumer);
-        }
-
-        public synchronized void register(AlarmManager alarmManager,
-                ICancellationSignal remoteCancellationSignal) {
-            if (mConsumer == null) {
-                return;
-            }
-
-            mAlarmManager = alarmManager;
-            mAlarmManager.set(
-                    ELAPSED_REALTIME,
-                    SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_MAX_TIMEOUT_MS,
-                    "GetCurrentLocation",
-                    this,
-                    null);
-
             mRemoteCancellationSignal = remoteCancellationSignal;
         }
 
         public void cancel() {
-            remove();
-        }
-
-        private Consumer<Location> remove() {
-            Consumer<Location> consumer;
             ICancellationSignal cancellationSignal;
             synchronized (this) {
-                mExecutor = null;
-                consumer = mConsumer;
-                mConsumer = null;
-
-                if (mAlarmManager != null) {
-                    mAlarmManager.cancel(this);
-                    mAlarmManager = null;
-                }
-
-                // ensure only one cancel event will go through
                 cancellationSignal = mRemoteCancellationSignal;
+                mConsumer = null;
                 mRemoteCancellationSignal = null;
             }
 
@@ -2472,72 +2421,76 @@
                 try {
                     cancellationSignal.cancel();
                 } catch (RemoteException e) {
-                    // ignore
+                    e.rethrowFromSystemServer();
                 }
             }
-
-            return consumer;
-        }
-
-        public void fail() {
-            deliverResult(null);
         }
 
         @Override
-        public void onAlarm() {
+        public void onLocation(@Nullable Location location) {
+            Consumer<Location> consumer;
             synchronized (this) {
-                // save ourselves a pointless x-process call to cancel the alarm
-                mAlarmManager = null;
-            }
-
-            deliverResult(null);
-        }
-
-        @Override
-        public void onLocationChanged(Location location) {
-            synchronized (this) {
-                // save ourselves a pointless x-process call to cancel the location request
+                consumer = mConsumer;
+                mConsumer = null;
                 mRemoteCancellationSignal = null;
             }
 
-            deliverResult(location);
+            executeSafely(mExecutor, () -> consumer, listener -> listener.accept(location));
+        }
+    }
+
+    private static class LocationListenerTransport extends ILocationListener.Stub implements
+            ListenerExecutor {
+
+        private Executor mExecutor;
+        @Nullable private volatile LocationListener mListener;
+
+        LocationListenerTransport(LocationListener listener, Executor executor) {
+            Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+            mListener = listener;
+            setExecutor(executor);
+        }
+
+        void setExecutor(Executor executor) {
+            Preconditions.checkArgument(executor != null, "invalid null executor");
+            mExecutor = executor;
+        }
+
+        void unregister() {
+            mListener = null;
         }
 
         @Override
-        public void onProviderEnabled(String provider) {}
+        public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+            executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
+                @Override
+                public void operate(LocationListener listener) {
+                    listener.onLocationChanged(location);
+                }
 
-        @Override
-        public void onProviderDisabled(String provider) {
-            // in the event of the provider being disabled it is unlikely that we will get further
-            // locations, so fail early so the client isn't left waiting hopelessly
-            deliverResult(null);
+                @Override
+                public void onComplete(boolean success) {
+                    markComplete(onCompleteCallback);
+                }
+            });
         }
 
         @Override
-        public void onRemoved() {
-            deliverResult(null);
+        public void onProviderEnabledChanged(String provider, boolean enabled) {
+            executeSafely(mExecutor, () -> mListener, listener -> {
+                if (enabled) {
+                    listener.onProviderEnabled(provider);
+                } else {
+                    listener.onProviderDisabled(provider);
+                }
+            });
         }
 
-        private synchronized void deliverResult(@Nullable Location location) {
-            if (mExecutor == null) {
-                return;
-            }
-
-            PooledRunnable runnable =
-                    obtainRunnable(GetCurrentLocationTransport::acceptResult, this, location)
-                            .recycleOnUse();
+        private void markComplete(IRemoteCallback onCompleteCallback) {
             try {
-                mExecutor.execute(runnable);
-            } catch (RejectedExecutionException e) {
-                runnable.recycle();
-                throw e;
-            }
-        }
-
-        private void acceptResult(Location location) {
-            Consumer<Location> consumer = remove();
-            if (consumer != null) {
-                consumer.accept(location);
+                onCompleteCallback.sendResult(null);
+            } catch (RemoteException e) {
+                e.rethrowFromSystemServer();
             }
         }
     }
diff --git a/location/java/android/location/util/LocationListenerTransportManager.java b/location/java/android/location/util/LocationListenerTransportManager.java
deleted file mode 100644
index f16f7e1..0000000
--- a/location/java/android/location/util/LocationListenerTransportManager.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.location.util;
-
-import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.location.ILocationListener;
-import android.location.ILocationManager;
-import android.location.Location;
-import android.location.LocationListener;
-import android.location.LocationRequest;
-import android.os.RemoteException;
-
-import com.android.internal.listeners.ListenerTransportManager;
-import com.android.internal.listeners.RequestListenerTransport;
-
-import java.util.concurrent.Executor;
-
-/**
- * Utility class for managing location listeners.
- *
- * @hide
- */
-public class LocationListenerTransportManager extends
-        ListenerTransportManager<LocationListenerTransportManager.LocationListenerTransport> {
-
-    protected class LocationListenerTransport extends
-            RequestListenerTransport<LocationRequest, LocationListener> {
-
-        private final ILocationListener mBinderTransport;
-
-        private final String mPackageName;
-        @Nullable private final String mAttributionTag;
-        private final String mListenerId;
-
-        LocationListenerTransport(Context context, LocationRequest locationRequest,
-                Executor executor, LocationListener locationListener) {
-            super(locationRequest, executor, locationListener);
-
-            mBinderTransport = new LocationListenerBinder(this);
-
-            mPackageName = context.getPackageName();
-            mAttributionTag = context.getAttributionTag();
-            mListenerId = AppOpsManager.toReceiverId(locationListener);
-        }
-
-        ILocationListener getTransport() {
-            return mBinderTransport;
-        }
-
-        String getPackageName() {
-            return mPackageName;
-        }
-
-        String getAttributionTag() {
-            return mAttributionTag;
-        }
-
-        String getListenerId() {
-            return mListenerId;
-        }
-
-        public void onLocationChanged(Location location) {
-            execute(listener -> {
-                listener.onLocationChanged(location);
-                try {
-                    mService.locationCallbackFinished(mBinderTransport);
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
-                }
-            });
-        }
-
-        public void onProviderEnabled(String provider) {
-            execute(listener -> {
-                listener.onProviderEnabled(provider);
-                try {
-                    mService.locationCallbackFinished(mBinderTransport);
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
-                }
-            });
-        }
-
-        public void onProviderDisabled(String provider) {
-            execute(listener -> {
-                listener.onProviderDisabled(provider);
-                try {
-                    mService.locationCallbackFinished(mBinderTransport);
-                } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
-                }
-            });
-        }
-
-        public void onRemoved() {
-            // must be executed on the same executor so callbacks cannot be reordered
-            execute(listener -> removeTransport(listener, this));
-        }
-    }
-
-    final ILocationManager mService;
-
-    public LocationListenerTransportManager(ILocationManager service) {
-        mService = service;
-    }
-
-    /** Adds the given listener. */
-    public void addListener(Context context, LocationRequest locationRequest, Executor executor,
-            LocationListener listener) {
-        registerListener(listener,
-                new LocationListenerTransport(context, locationRequest, executor, listener));
-    }
-
-    /** Removes the given listener. */
-    public void removeListener(LocationListener listener) {
-        unregisterListener(listener);
-    }
-
-    @Override
-    protected void registerWithServer(LocationListenerTransport transport) throws RemoteException {
-        mService.registerLocationListener(transport.getRequest(), transport.getTransport(),
-                transport.getPackageName(), transport.getAttributionTag(),
-                transport.getListenerId());
-    }
-
-    @Override
-    protected void unregisterWithServer(LocationListenerTransport transport)
-            throws RemoteException {
-        mService.unregisterLocationListener(transport.getTransport());
-    }
-
-    private static class LocationListenerBinder extends ILocationListener.Stub {
-
-        private final LocationListenerTransport mListener;
-
-        LocationListenerBinder(LocationListenerTransport listener) {
-            mListener = listener;
-        }
-
-        @Override
-        public void onLocationChanged(Location location) {
-            mListener.onLocationChanged(location);
-        }
-
-        @Override
-        public void onProviderEnabled(String provider) {
-            mListener.onProviderEnabled(provider);
-        }
-
-        @Override
-        public void onProviderDisabled(String provider) {
-            mListener.onProviderDisabled(provider);
-        }
-
-        @Override
-        public void onRemoved() {
-            mListener.onRemoved();
-        }
-    }
-}
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 197786f..c2168f1 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -654,6 +654,9 @@
          * <p>
          *
          * The following table summarizes the format keys considered by this method.
+         * This is especially important to consider when targeting a higher SDK version than the
+         * minimum SDK version, as this method will disregard some keys on devices below the target
+         * SDK version.
          *
          * <table style="width: 0%">
          *  <thead>
@@ -668,7 +671,7 @@
          *  </thead>
          *  <tbody>
          *   <tr>
-         *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</th>
+         *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td>
          *    <td rowspan=3>{@link MediaFormat#KEY_MIME}<sup>*</sup>,<br>
          *        {@link MediaFormat#KEY_SAMPLE_RATE},<br>
          *        {@link MediaFormat#KEY_CHANNEL_COUNT},</td>
@@ -679,30 +682,51 @@
          *        {@link MediaFormat#KEY_WIDTH},<br>
          *        {@link MediaFormat#KEY_HEIGHT},<br>
          *        <strong>no</strong> {@code KEY_FRAME_RATE}</td>
-         *    <td rowspan=4>{@link MediaFormat#KEY_BITRATE_MODE},<br>
+         *    <td rowspan=10>as to the left, plus<br>
+         *        {@link MediaFormat#KEY_BITRATE_MODE},<br>
          *        {@link MediaFormat#KEY_PROFILE}
          *        (and/or {@link MediaFormat#KEY_AAC_PROFILE}<sup>~</sup>),<br>
          *        <!-- {link MediaFormat#KEY_QUALITY},<br> -->
          *        {@link MediaFormat#KEY_COMPLEXITY}
          *        (and/or {@link MediaFormat#KEY_FLAC_COMPRESSION_LEVEL}<sup>~</sup>)</td>
          *   </tr><tr>
-         *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</th>
+         *    <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td>
          *    <td rowspan=2>as above, plus<br>
          *        {@link MediaFormat#KEY_FRAME_RATE}</td>
          *   </tr><tr>
-         *    <td>{@link android.os.Build.VERSION_CODES#M}</th>
+         *    <td>{@link android.os.Build.VERSION_CODES#M}</td>
          *   </tr><tr>
-         *    <td>{@link android.os.Build.VERSION_CODES#N}</th>
-         *    <td>as above, plus<br>
+         *    <td>{@link android.os.Build.VERSION_CODES#N}</td>
+         *    <td rowspan=2>as above, plus<br>
          *        {@link MediaFormat#KEY_PROFILE},<br>
          *        <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
          *        {@link MediaFormat#KEY_BIT_RATE}</td>
-         *    <td>as above, plus<br>
+         *    <td rowspan=2>as above, plus<br>
          *        {@link MediaFormat#KEY_PROFILE},<br>
          *        {@link MediaFormat#KEY_LEVEL}<sup>+</sup>,<br>
          *        <!-- {link MediaFormat#KEY_MAX_BIT_RATE},<br> -->
          *        {@link MediaFormat#KEY_BIT_RATE},<br>
          *        {@link CodecCapabilities#FEATURE_IntraRefresh}<sup>E</sup></td>
+         *   </tr><tr>
+         *    <td>{@link android.os.Build.VERSION_CODES#N_MR1}</td>
+         *   </tr><tr>
+         *    <td>{@link android.os.Build.VERSION_CODES#O}</td>
+         *    <td rowspan=3 colspan=2>as above, plus<br>
+         *        {@link CodecCapabilities#FEATURE_PartialFrame}<sup>D</sup></td>
+         *   </tr><tr>
+         *    <td>{@link android.os.Build.VERSION_CODES#O_MR1}</td>
+         *   </tr><tr>
+         *    <td>{@link android.os.Build.VERSION_CODES#P}</td>
+         *   </tr><tr>
+         *    <td>{@link android.os.Build.VERSION_CODES#Q}</td>
+         *    <td colspan=2>as above, plus<br>
+         *        {@link CodecCapabilities#FEATURE_FrameParsing}<sup>D</sup>,<br>
+         *        {@link CodecCapabilities#FEATURE_MultipleFrames},<br>
+         *        {@link CodecCapabilities#FEATURE_DynamicTimestamp}</td>
+         *   </tr><tr>
+         *    <td>{@link android.os.Build.VERSION_CODES#R}</td>
+         *    <td colspan=2>as above, plus<br>
+         *        {@link CodecCapabilities#FEATURE_LowLatency}<sup>D</sup></td>
          *   </tr>
          *   <tr>
          *    <td colspan=4>
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index fad25e0..7e9d2d8 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -313,6 +313,14 @@
     public static final String FEATURE_REMOTE_VIDEO_PLAYBACK =
             "android.media.route.feature.REMOTE_VIDEO_PLAYBACK";
 
+    /**
+     * Route feature: Remote group playback.
+     * <p>
+     * @hide
+     */
+    public static final String FEATURE_REMOTE_GROUP_PLAYBACK =
+            "android.media.route.feature.REMOTE_GROUP_PLAYBACK";
+
     final String mId;
     final CharSequence mName;
     final List<String> mFeatures;
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index efeb335..f4d65d0 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -25,6 +25,7 @@
 #include <limits.h>
 
 #include <audio_utils/fixedfft.h>
+#include <cutils/bitops.h>
 #include <utils/Thread.h>
 
 #include "Visualizer.h"
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 53499f8..e3aceec 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -2465,6 +2465,7 @@
 
   public final class HdmiControlManager {
     method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
+    method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
     method @Nullable public android.hardware.hdmi.HdmiClient getClient(int);
     method @NonNull public java.util.List<android.hardware.hdmi.HdmiDeviceInfo> getConnectedDevices();
     method public int getPhysicalAddress();
diff --git a/packages/SettingsLib/res/drawable/ic_media_display_device.xml b/packages/SettingsLib/res/drawable/ic_media_display_device.xml
index 78b4e2a..54fec78 100644
--- a/packages/SettingsLib/res/drawable/ic_media_display_device.xml
+++ b/packages/SettingsLib/res/drawable/ic_media_display_device.xml
@@ -15,16 +15,12 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14dp"
-        android:height="11dp"
-        android:viewportWidth="14"
-        android:viewportHeight="11"
-        android:tint="?android:attr/colorControlNormal">
+        android:width="18dp"
+        android:height="18dp"
+        android:viewportWidth="18"
+        android:viewportHeight="18">
     <path
-        android:pathData="M10,10v1H4v-1H1.5A1.5,1.5 0,0 1,0 8.5v-7A1.5,1.5 0,
-        0 1,1.5 0h11A1.5,1.5 0,0 1,14 1.5v7a1.5,1.5 0,0 1,-1.5 1.5H10zM1.5,
-        1a0.5,0.5 0,0 0,-0.5 0.5v7a0.5,0.5 0,0 0,0.5 0.5h11a0.5,0.5 0,0 0,
-        0.5 -0.5v-7a0.5,0.5 0,0 0,-0.5 -0.5h-11z"
-        android:fillColor="#000000"
+        android:pathData="M12,14V15H6V14H3.5C3.1022,14 2.7206,13.842 2.4393,13.5607C2.158,13.2794 2,12.8978 2,12.5V5.5C2,5.1022 2.158,4.7206 2.4393,4.4393C2.7206,4.158 3.1022,4 3.5,4H14.5C14.8978,4 15.2794,4.158 15.5607,4.4393C15.842,4.7206 16,5.1022 16,5.5V12.5C16,12.8978 15.842,13.2794 15.5607,13.5607C15.2794,13.842 14.8978,14 14.5,14H12ZM3.5,5C3.3674,5 3.2402,5.0527 3.1465,5.1465C3.0527,5.2402 3,5.3674 3,5.5V12.5C3,12.6326 3.0527,12.7598 3.1465,12.8536C3.2402,12.9473 3.3674,13 3.5,13H14.5C14.6326,13 14.7598,12.9473 14.8536,12.8536C14.9473,12.7598 15,12.6326 15,12.5V5.5C15,5.3674 14.9473,5.2402 14.8536,5.1465C14.7598,5.0527 14.6326,5 14.5,5H3.5Z"
+        android:fillColor="#5F6368"
         android:fillType="evenOdd"/>
 </vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index 231809b..e5fd0ba 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -32,12 +32,11 @@
 import androidx.core.text.BidiFormatter;
 import androidx.core.text.TextDirectionHeuristicsCompat;
 
+import com.android.i18n.timezone.CountryTimeZones;
+import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping;
+import com.android.i18n.timezone.TimeZoneFinder;
 import com.android.settingslib.R;
 
-import libcore.timezone.CountryTimeZones;
-import libcore.timezone.CountryTimeZones.TimeZoneMapping;
-import libcore.timezone.TimeZoneFinder;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.util.ArrayList;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index ea71e52d..949b245 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -15,6 +15,8 @@
  */
 package com.android.settingslib.media;
 
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK;
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
@@ -29,6 +31,8 @@
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.BluetoothUtils;
 
+import java.util.List;
+
 /**
  * InfoMediaDevice extends MediaDevice to represents wifi device.
  */
@@ -62,7 +66,7 @@
 
     @Override
     public Drawable getIconWithoutBackground() {
-        return mContext.getDrawable(getDrawableResId());
+        return mContext.getDrawable(getDrawableResIdByFeature());
     }
 
     @VisibleForTesting
@@ -83,6 +87,21 @@
         return resId;
     }
 
+    @VisibleForTesting
+    int getDrawableResIdByFeature() {
+        int resId;
+        final List<String> features = mRouteInfo.getFeatures();
+        if (features.contains(FEATURE_REMOTE_GROUP_PLAYBACK)) {
+            resId = R.drawable.ic_media_group_device;
+        } else if (features.contains(FEATURE_REMOTE_VIDEO_PLAYBACK)) {
+            resId = R.drawable.ic_media_display_device;
+        } else {
+            resId = R.drawable.ic_media_speaker_device;
+        }
+
+        return resId;
+    }
+
     @Override
     public String getId() {
         return MediaDeviceUtils.getId(mRouteInfo);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index d7e76a1..b7ae3dc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -214,6 +214,9 @@
     }
 
     private void updateStatusLabel() {
+        if (mWifiManager == null) {
+            return;
+        }
         NetworkCapabilities networkCapabilities;
         final Network currentWifiNetwork = mWifiManager.getCurrentNetwork();
         if (currentWifiNetwork != null && currentWifiNetwork.equals(mDefaultNetwork)) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 49b236a..c45b7f3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -16,6 +16,9 @@
 
 package com.android.settingslib.media;
 
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_GROUP_PLAYBACK;
+import static android.media.MediaRoute2Info.FEATURE_REMOTE_VIDEO_PLAYBACK;
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
@@ -38,6 +41,8 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.ArrayList;
+
 @RunWith(RobolectricTestRunner.class)
 public class InfoMediaDeviceTest {
 
@@ -107,4 +112,28 @@
 
         assertThat(mInfoMediaDevice.getDrawableResId()).isEqualTo(R.drawable.ic_media_group_device);
     }
+
+    @Test
+    public void getDrawableResIdByFeature_returnCorrectResId() {
+        final ArrayList<String> features = new ArrayList<>();
+        features.add(FEATURE_REMOTE_VIDEO_PLAYBACK);
+        when(mRouteInfo.getFeatures()).thenReturn(features);
+
+        assertThat(mInfoMediaDevice.getDrawableResIdByFeature()).isEqualTo(
+                R.drawable.ic_media_display_device);
+
+        features.clear();
+        features.add(FEATURE_REMOTE_AUDIO_PLAYBACK);
+        when(mRouteInfo.getFeatures()).thenReturn(features);
+
+        assertThat(mInfoMediaDevice.getDrawableResIdByFeature()).isEqualTo(
+                R.drawable.ic_media_speaker_device);
+
+        features.clear();
+        features.add(FEATURE_REMOTE_GROUP_PLAYBACK);
+        when(mRouteInfo.getFeatures()).thenReturn(features);
+
+        assertThat(mInfoMediaDevice.getDrawableResIdByFeature()).isEqualTo(
+                R.drawable.ic_media_group_device);
+    }
 }
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
new file mode 100644
index 0000000..f474a36
--- /dev/null
+++ b/packages/SystemUI/res/layout/udfps_view.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.systemui.udfps.UdfpsView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/udfps_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    systemui:sensorRadius="140px"
+    systemui:sensorMarginBottom="630px"
+    systemui:sensorTouchAreaCoefficient="0.5"/>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 01efbef..1af73a0 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -721,7 +721,7 @@
     <string name="inline_turn_off_notifications" msgid="8543989584403106071">"إيقاف الإشعارات"</string>
     <string name="inline_keep_showing_app" msgid="4393429060390649757">"هل تريد الاستمرار في تلقي إشعارات من هذا التطبيق؟"</string>
     <string name="notification_silence_title" msgid="8608090968400832335">"صامتة"</string>
-    <string name="notification_alert_title" msgid="3656229781017543655">"تلقائي"</string>
+    <string name="notification_alert_title" msgid="3656229781017543655">"تلقائية"</string>
     <string name="notification_bubble_title" msgid="8330481035191903164">"فقاعة"</string>
     <string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
     <string name="notification_channel_summary_low" msgid="4860617986908931158">"بدون صوت أو اهتزاز"</string>
@@ -1052,7 +1052,7 @@
     <string name="priority_onboarding_settings_button_title" msgid="6663601574303585927">"الإعدادات"</string>
     <string name="magnification_window_title" msgid="4863914360847258333">"نافذة التكبير"</string>
     <string name="magnification_controls_title" msgid="8421106606708891519">"عناصر التحكم في نافذة التكبير"</string>
-    <string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالجهاز"</string>
+    <string name="quick_controls_title" msgid="6839108006171302273">"أدوات التحكم بالأجهزة"</string>
     <string name="quick_controls_subtitle" msgid="1667408093326318053">"إضافة عناصر تحكّم لأجهزتك المتصلة"</string>
     <string name="quick_controls_setup_title" msgid="8901436655997849822">"إعداد أدوات التحكم بالجهاز"</string>
     <string name="quick_controls_setup_subtitle" msgid="1681506617879773824">"اضغط مع الاستمرار على زر التشغيل للوصول إلى عناصر التحكّم"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 1f1606d..d750bc9 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"নিয়ন্ত্ৰণসমূহ পুনৰ সজাবলৈ ধৰি ৰাখক আৰু টানি আনি এৰক"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"সকলো নিয়ন্ত্ৰণ আঁতৰোৱা হৈছে"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"সালসলনিসমূহ ছেভ নহ’ল"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"অন্য এপ্‌সমূহ চাওক"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"নিয়ন্ত্ৰণসমূহ ল’ড কৰিবপৰা নগ’ল। এপ্‌টোৰ ছেটিংসমূহ সলনি কৰা হোৱা নাই বুলি নিশ্চিত কৰিবলৈ <xliff:g id="APP">%s</xliff:g> এপ্‌টো পৰীক্ষা কৰক।"</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"সমিল নিয়ন্ত্ৰণসমূহ উপলব্ধ নহয়"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"অন্য"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 9d9c3b4..d9cfd9d 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -628,9 +628,7 @@
     <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Isključi zvuk"</string>
     <string name="qs_status_phone_vibrate" msgid="7055409506885541979">"Na telefonu je uključena vibracija"</string>
     <string name="qs_status_phone_muted" msgid="3763664791309544103">"Zvuk na telefonu je isključen"</string>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for volume_stream_content_description_unmute (7729576371406792977) -->
-    <skip />
+    <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Dodirnite da uključite zvukove."</string>
     <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Dodirnite za postavljanje vibracije. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
     <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Dodirnite da isključite zvuk. Zvukovi usluga pristupačnosti mogu biti isključeni."</string>
     <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Dodirnite da postavite vibraciju."</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index c5e7778..5c91a17 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"નિયંત્રણોને ફરીથી ગોઠવવા માટે તેમને હોલ્ડ કરીને ખેંચો"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"બધા નિયંત્રણો કાઢી નાખ્યા"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ફેરફારો સાચવ્યા નથી"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"અન્ય બધી ઍપ જુઓ"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"નિયંત્રણો લોડ કરી શકાયા નથી. ઍપના સેટિંગ બદલાયા નથી તેની ખાતરી કરવા માટે <xliff:g id="APP">%s</xliff:g> ઍપ ચેક કરો."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"સુસંગત નિયંત્રણો ઉપલબ્ધ નથી"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"અન્ય"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 37a012d..bdd2052f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -1055,7 +1055,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"कंट्रोल का क्रम फिर से बदलने के लिए उन्हें दबाकर रखें और खींचें"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"सभी कंट्रोल हटा दिए गए"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"बदलाव सेव नहीं किए गए"</string>
-    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"अन्य ऐप्लिकेशन देखें"</string>
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"दूसरे ऐप्लिकेशन देखें"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"कंट्रोल लोड नहीं किए जा सके. <xliff:g id="APP">%s</xliff:g> ऐप्लिकेशन देखें, ताकि यह पक्का किया जा सके कि ऐप्लिकेशन की सेटिंग में कोई बदलाव नहीं हुआ है."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"इस सेटिंग के साथ काम करने वाले कंट्रोल उपलब्ध नहीं हैं"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"अन्य"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 4490d23..1f4d903 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"ನಿಯಂತ್ರಣಗಳನ್ನು ಮರುಹೊಂದಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"ಎಲ್ಲಾ ನಿಯಂತ್ರಣಗಳನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ಬದಲಾವಣೆಗಳನ್ನು ಉಳಿಸಲಾಗಿಲ್ಲ"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ಇತರ ಆ್ಯಪ್‌ಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"ನಿಯಂತ್ರಣಗಳನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ಆ್ಯಪ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಬದಲಾಗಿಲ್ಲ ಎಂದು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು <xliff:g id="APP">%s</xliff:g> ಆ್ಯಪ್ ಅನ್ನು ಪರಿಶೀಲಿಸಿ."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"ಹೊಂದಾಣಿಕೆಯ ನಿಯಂತ್ರಣಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ಇತರ"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 3decb1e..34f1ac6 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"നിയന്ത്രണങ്ങൾ പുനഃക്രമീകരിക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"എല്ലാ നിയന്ത്രണങ്ങളും നീക്കം ചെയ്തു"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"മാറ്റങ്ങൾ സംരക്ഷിച്ചിട്ടില്ല"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"മറ്റ് ആപ്പുകൾ കാണുക"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"നിയന്ത്രണങ്ങൾ ലോഡ് ചെയ്യാനായില്ല. ആപ്പ് ക്രമീകരണം മാറ്റിയിട്ടില്ലെന്ന് ഉറപ്പാക്കാൻ <xliff:g id="APP">%s</xliff:g> ആപ്പ് പരിശോധിക്കുക."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"അനുയോജ്യമായ നിയന്ത്രണങ്ങൾ ലഭ്യമല്ല"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"മറ്റുള്ളവ"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index e41c6d8..90a937e 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"नियंत्रणांची पुनर्रचना करण्यासाठी धरून ठेवा आणि ड्रॅग करा"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"सर्व नियंत्रणे काढून टाकली आहेत"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"बदल सेव्ह केले गेले नाहीत"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"इतर अ‍ॅप्स पहा"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"नियंत्रणे लोड करता अली नाहीत. ॲपची सेटिंग्ज बदलली नसल्याची खात्री करण्यासाठी <xliff:g id="APP">%s</xliff:g> ॲप तपासा."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"कंपॅटिबल नियंत्रणे उपलब्ध नाहीत"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"इतर"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 9d298b9..4d0d2ef 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"नियन्त्रणहरूको क्रम मिलाउन तिनलाई थिचेर ड्र्याग गर्नुहोस्"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"सबै नियन्त्रणहरू हटाइए"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"परिवर्तनहरू सुरक्षित गरिएका छैनन्"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"अन्य एपहरू हेर्नुहोस्"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"नियन्त्रण सुविधाहरू लोड गर्न सकिएन। <xliff:g id="APP">%s</xliff:g> एपका सेटिङ परिवर्तन गरिएका छैनन् भन्ने कुरा सुनिश्चित गर्न उक्त एप जाँच्नुहोस्।"</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"मिल्दा नियन्त्रण सुविधाहरू उपलब्ध छैनन्"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"अन्य"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index d7144ac..519606d 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"ਕੰਟਰੋਲਾਂ ਨੂੰ ਮੁੜ-ਵਿਵਸਥਿਤ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"ਸਾਰੇ ਕੰਟਰੋਲ ਹਟਾਏ ਗਏ"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"ਤਬਦੀਲੀਆਂ ਨੂੰ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ਹੋਰ ਐਪਾਂ ਦੇਖੋ"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"ਕੰਟਰੋਲਾਂ ਨੂੰ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਇਹ ਪੱਕਾ ਕਰਨ ਲਈ <xliff:g id="APP">%s</xliff:g> ਐਪ ਦੀ ਜਾਂਚ ਕਰੋ ਕਿ ਐਪ ਸੈਟਿੰਗਾਂ ਨਹੀਂ ਬਦਲੀਆਂ ਹਨ।"</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"ਕੋਈ ਅਨੁਰੂਪ ਕੰਟਰੋਲ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ਹੋਰ"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index ddcbf45..ccca95d 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -1053,8 +1053,7 @@
     <string name="controls_favorite_rearrange" msgid="5616952398043063519">"నియంత్రణల క్రమం మార్చడానికి దేనినైనా పట్టుకుని, లాగి వదిలేయండి"</string>
     <string name="controls_favorite_removed" msgid="5276978408529217272">"అన్ని నియంత్రణలు తీసివేయబడ్డాయి"</string>
     <string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"మార్పులు సేవ్ చేయబడలేదు"</string>
-    <!-- no translation found for controls_favorite_see_other_apps (7709087332255283460) -->
-    <skip />
+    <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"ఇతర యాప్‌లను చూడండి"</string>
     <string name="controls_favorite_load_error" msgid="5126216176144877419">"కంట్రోల్‌లను లోడ్ చేయడం సాధ్యపడలేదు. యాప్ సెట్టింగ్‌లు మారలేదని నిర్ధారించడానికి <xliff:g id="APP">%s</xliff:g> యాప్‌ను చెక్ చేయండి."</string>
     <string name="controls_favorite_load_none" msgid="7687593026725357775">"అనుకూల కంట్రోల్‌లు అందుబాటులో లేవు"</string>
     <string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"ఇతరం"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index c419594..10c7210 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -161,5 +161,12 @@
         <attr name="rippleMinSize" format="dimension" />
         <attr name="rippleMaxSize" format="dimension" />
     </declare-styleable>
+
+    <declare-styleable name="UdfpsView">
+        <attr name="sensorRadius" format="dimension"/>
+        <attr name="sensorMarginBottom" format="dimension"/>
+        <attr name="sensorPressureCoefficient" format="float"/>
+        <attr name="sensorTouchAreaCoefficient" format="float"/>
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 66ef747..82c111f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2840,4 +2840,11 @@
     <string name="controls_menu_add">Add controls</string>
     <!-- Controls menu, edit [CHAR_LIMIT=30] -->
     <string name="controls_menu_edit">Edit controls</string>
+
+    <!-- Device-specific path to the node for enabling/disabling the high-brightness mode -->
+    <string name="udfps_hbm_sysfs_path" translatable="false"></string>
+    <!-- Device-specific payload for enabling the high-brightness mode -->
+    <string name="udfps_hbm_enable_command" translatable="false"></string>
+    <!-- Device-specific payload for disabling the high-brightness mode -->
+    <string name="udfps_hbm_disable_command" translatable="false"></string>
 </resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index b39eaf3..e45cfe2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2047,8 +2047,8 @@
                 mFingerprintCancelSignal.cancel();
             }
             mFingerprintCancelSignal = new CancellationSignal();
-            mFpm.authenticate(null, mFingerprintCancelSignal, 0, mFingerprintAuthenticationCallback,
-                    null, userId);
+            mFpm.authenticate(null /* crypto */, mFingerprintCancelSignal,
+                    mFingerprintAuthenticationCallback, null /* handler */, userId);
             setFingerprintRunningState(BIOMETRIC_STATE_RUNNING);
         }
     }
@@ -2065,8 +2065,8 @@
                 mFaceCancelSignal.cancel();
             }
             mFaceCancelSignal = new CancellationSignal();
-            mFaceManager.authenticate(null, mFaceCancelSignal, 0,
-                    mFaceAuthenticationCallback, null, userId);
+            mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,
+                    mFaceAuthenticationCallback, null /* handler */, userId);
             setFaceRunningState(BIOMETRIC_STATE_RUNNING);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 81439e18..7b441d6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
@@ -37,10 +38,12 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
 import android.view.WindowManager;
 
@@ -76,6 +79,7 @@
 
     private Handler mHandler = new Handler(Looper.getMainLooper());
     private WindowManager mWindowManager;
+    private UdfpsController mUdfpsController;
     @VisibleForTesting
     IActivityTaskManager mActivityTaskManager;
     @VisibleForTesting
@@ -242,6 +246,11 @@
         IActivityTaskManager getActivityTaskManager() {
             return ActivityTaskManager.getService();
         }
+
+        IFingerprintService getFingerprintService() {
+            return IFingerprintService.Stub.asInterface(
+                    ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+        }
     }
 
     @Inject
@@ -267,6 +276,26 @@
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mActivityTaskManager = mInjector.getActivityTaskManager();
 
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+            IFingerprintService fingerprintService = mInjector.getFingerprintService();
+            if (fingerprintService == null) {
+                Log.e(TAG, "FEATURE_FINGERPRINT is available, but FingerprintService is null");
+            } else {
+                boolean isUdfps = false;
+                try {
+                    // TODO(b/160024833): Enumerate through all of the sensors and check whether
+                    //  at least one of them is UDFPS.
+                    isUdfps = fingerprintService.isUdfps(0 /* sensorId */);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Unable to check whether the sensor is a UDFPS", e);
+                }
+                if (isUdfps) {
+                    mUdfpsController = new UdfpsController(mContext, fingerprintService,
+                            mWindowManager);
+                }
+            }
+        }
+
         try {
             mTaskStackListener = new BiometricTaskStackListener();
             mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
new file mode 100644
index 0000000..c82e1de
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IUdfpsOverlayController;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
+ * and coordinates triggering of the high-brightness mode (HBM).
+ */
+class UdfpsController {
+    private static final String TAG = "UdfpsController";
+
+    private final Context mContext;
+    private final IFingerprintService mFingerprintService;
+    private final WindowManager mWindowManager;
+
+    private UdfpsView mView;
+    private WindowManager.LayoutParams mLayoutParams;
+    private String mHbmPath;
+    private String mHbmEnableCommand;
+    private String mHbmDisableCommand;
+    private boolean mIsOverlayShowing;
+
+    public class UdfpsOverlayController extends IUdfpsOverlayController.Stub {
+        @Override
+        public void showUdfpsOverlay() {
+            UdfpsController.this.showUdfpsOverlay();
+        }
+
+        @Override
+        public void hideUdfpsOverlay() {
+            UdfpsController.this.hideUdfpsOverlay();
+        }
+    }
+
+    @SuppressLint("ClickableViewAccessibility")
+    private final UdfpsView.OnTouchListener mOnTouchListener = (v, event) -> {
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_MOVE:
+                boolean isValidTouch = mView.isValidTouch(event.getX(), event.getY(),
+                        event.getPressure());
+                if (!mView.isFingerDown() && isValidTouch) {
+                    onFingerDown((int) event.getX(), (int) event.getY(), event.getTouchMinor(),
+                            event.getTouchMajor());
+                } else if (mView.isFingerDown() && !isValidTouch) {
+                    onFingerUp();
+                }
+                return true;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                if (mView.isFingerDown()) {
+                    onFingerUp();
+                }
+                return true;
+
+            default:
+                return false;
+        }
+    };
+
+    UdfpsController(Context context, IFingerprintService fingerprintService,
+            WindowManager windowManager) {
+        mContext = context;
+        mFingerprintService = fingerprintService;
+        mWindowManager = windowManager;
+        start();
+    }
+
+    private void start() {
+        Log.v(TAG, "start");
+
+        Point displaySize = new Point();
+        mWindowManager.getDefaultDisplay().getRealSize(displaySize);
+        // TODO(b/160025856): move to the "dump" method.
+        Log.v(TAG, "UdfpsController | display size: " + displaySize.x + "x"
+                + displaySize.y);
+
+        mLayoutParams = new WindowManager.LayoutParams(
+                displaySize.x,
+                displaySize.y,
+                // TODO(b/152419866): Use the UDFPS window type when it becomes available.
+                WindowManager.LayoutParams.TYPE_BOOT_PROGRESS,
+                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
+                PixelFormat.TRANSLUCENT);
+        mLayoutParams.setTitle(TAG);
+        mLayoutParams.windowAnimations = 0;
+
+        LinearLayout layout = new LinearLayout(mContext);
+        layout.setLayoutParams(mLayoutParams);
+        mView = (UdfpsView) LayoutInflater.from(mContext).inflate(R.layout.udfps_view, layout,
+                false);
+        mView.setOnTouchListener(mOnTouchListener);
+
+        mHbmPath = mContext.getResources().getString(R.string.udfps_hbm_sysfs_path);
+        mHbmEnableCommand = mContext.getResources().getString(R.string.udfps_hbm_enable_command);
+        mHbmDisableCommand = mContext.getResources().getString(R.string.udfps_hbm_disable_command);
+
+        try {
+            mFingerprintService.setUdfpsOverlayController(new UdfpsOverlayController());
+        } catch (RemoteException e) {
+            Log.e(TAG, "start | failed to set UDFPS controller", e);
+        }
+        mIsOverlayShowing = false;
+    }
+
+    private void showUdfpsOverlay() {
+        Log.v(TAG, "showUdfpsOverlay | adding window");
+        if (!mIsOverlayShowing) {
+            try {
+                mWindowManager.addView(mView, mLayoutParams);
+                mIsOverlayShowing = true;
+            } catch (RuntimeException e) {
+                Log.e(TAG, "showUdfpsOverlay | failed to add window", e);
+            }
+        }
+    }
+
+    private void hideUdfpsOverlay() {
+        Log.v(TAG, "hideUdfpsOverlay | removing window");
+        if (mIsOverlayShowing) {
+            mWindowManager.removeView(mView);
+            mIsOverlayShowing = false;
+        }
+    }
+
+    private void onFingerDown(int x, int y, float minor, float major) {
+        try {
+            FileWriter fw = new FileWriter(mHbmPath);
+            fw.write(mHbmEnableCommand);
+            fw.close();
+        } catch (IOException e) {
+            Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
+        }
+        mView.onFingerDown();
+        try {
+            mFingerprintService.onFingerDown(x, y, minor, major);
+        } catch (RemoteException e) {
+            Log.e(TAG, "onFingerDown | failed to propagate onFingerDown", e);
+        }
+    }
+
+    private void onFingerUp() {
+        try {
+            mFingerprintService.onFingerUp();
+        } catch (RemoteException e) {
+            Log.e(TAG, "onFingeUp | failed to propagate onFingerUp", e);
+        }
+        mView.onFingerUp();
+        try {
+            FileWriter fw = new FileWriter(mHbmPath);
+            fw.write(mHbmDisableCommand);
+            fw.close();
+        } catch (IOException e) {
+            Log.e(TAG, "onFingerUp | failed to disable HBM: " + e.getMessage());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
new file mode 100644
index 0000000..dfc7d53
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver;
+
+import com.android.systemui.R;
+
+/**
+ * A full screen view with a configurable illumination dot and scrim.
+ */
+public class UdfpsView extends View {
+    private static final String TAG = "UdfpsView";
+
+    private final Rect mScrimRect;
+    private final Paint mScrimPaint;
+
+    private float mSensorX;
+    private float mSensorY;
+    private final RectF mSensorRect;
+    private final Paint mSensorPaint;
+    private final float mSensorRadius;
+    private final float mSensorMarginBottom;
+    private final float mSensorTouchAreaCoefficient;
+
+    private final Rect mTouchableRegion;
+    private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsListener;
+
+    private boolean mIsFingerDown;
+
+    public UdfpsView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.UdfpsView, 0,
+                0);
+        try {
+            if (!a.hasValue(R.styleable.UdfpsView_sensorRadius)) {
+                throw new IllegalArgumentException("UdfpsView must contain sensorRadius");
+            }
+            if (!a.hasValue(R.styleable.UdfpsView_sensorMarginBottom)) {
+                throw new IllegalArgumentException("UdfpsView must contain sensorMarginBottom");
+            }
+            if (!a.hasValue(R.styleable.UdfpsView_sensorTouchAreaCoefficient)) {
+                throw new IllegalArgumentException(
+                        "UdfpsView must contain sensorTouchAreaCoefficient");
+            }
+            mSensorRadius = a.getDimension(R.styleable.UdfpsView_sensorRadius, 0f);
+            mSensorMarginBottom = a.getDimension(R.styleable.UdfpsView_sensorMarginBottom, 0f);
+            mSensorTouchAreaCoefficient = a.getFloat(
+                    R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f);
+        } finally {
+            a.recycle();
+        }
+
+
+        mScrimRect = new Rect();
+        mScrimPaint = new Paint(0 /* flags */);
+        mScrimPaint.setARGB(110 /* a */, 0 /* r */, 0 /* g */, 0 /* b */);
+
+        mSensorRect = new RectF();
+        mSensorPaint = new Paint(0 /* flags */);
+        mSensorPaint.setColor(Color.WHITE);
+        mSensorPaint.setStyle(Paint.Style.STROKE);
+
+        mTouchableRegion = new Rect();
+        mInsetsListener = internalInsetsInfo -> {
+            internalInsetsInfo.setTouchableInsets(
+                    ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+            internalInsetsInfo.touchableRegion.set(mTouchableRegion);
+        };
+
+        mIsFingerDown = false;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        Log.v(TAG, "onAttachedToWindow");
+
+        final int h = getLayoutParams().height;
+        final int w = getLayoutParams().width;
+        mScrimRect.set(0 /* left */, 0 /* top */, w, h);
+        mSensorX = w / 2f;
+        mSensorY = h - mSensorMarginBottom - mSensorRadius;
+        mSensorRect.set(mSensorX - mSensorRadius, mSensorY - mSensorRadius,
+                mSensorX + mSensorRadius, mSensorY + mSensorRadius);
+        mSensorRect.roundOut(mTouchableRegion);
+
+        getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsListener);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        Log.v(TAG, "onDetachedFromWindow");
+        getViewTreeObserver().removeOnComputeInternalInsetsListener(mInsetsListener);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mIsFingerDown) {
+            canvas.drawRect(mScrimRect, mScrimPaint);
+        }
+        canvas.drawOval(mSensorRect, mSensorPaint);
+    }
+
+    boolean isValidTouch(float x, float y, float pressure) {
+        return x > (mSensorX - mSensorRadius * mSensorTouchAreaCoefficient)
+                && x < (mSensorX + mSensorRadius * mSensorTouchAreaCoefficient)
+                && y > (mSensorY - mSensorRadius * mSensorTouchAreaCoefficient)
+                && y < (mSensorY + mSensorRadius * mSensorTouchAreaCoefficient);
+    }
+
+    boolean isFingerDown() {
+        return mIsFingerDown;
+    }
+
+    void onFingerDown() {
+        mIsFingerDown = true;
+        mSensorPaint.setStyle(Paint.Style.FILL);
+        invalidate();
+    }
+
+    void onFingerUp() {
+        mIsFingerDown = false;
+        mSensorPaint.setStyle(Paint.Style.STROKE);
+        invalidate();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 6dc8322..c6d1286 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -124,8 +124,26 @@
     private int mNotificationId;
     private int mAppUid = -1;
 
+    /**
+     * A bubble is created and can be updated. This intent is updated until the user first
+     * expands the bubble. Once the user has expanded the contents, we ignore the intent updates
+     * to prevent restarting the intent & possibly altering UI state in the activity in front of
+     * the user.
+     *
+     * Once the bubble is overflowed, the activity is finished and updates to the
+     * notification are respected. Typically an update to an overflowed bubble would result in
+     * that bubble being added back to the stack anyways.
+     */
     @Nullable
     private PendingIntent mIntent;
+    private boolean mIntentActive;
+    @Nullable
+    private PendingIntent.CancelListener mIntentCancelListener;
+
+    /**
+     * Sent when the bubble & notification are no longer visible to the user (i.e. no
+     * notification in the shade, no bubble in the stack or overflow).
+     */
     @Nullable
     private PendingIntent mDeleteIntent;
 
@@ -150,13 +168,19 @@
         mShowBubbleUpdateDot = false;
     }
 
-    /** Used in tests when no UI is required. */
     @VisibleForTesting(visibility = PRIVATE)
     Bubble(@NonNull final NotificationEntry e,
-            @Nullable final BubbleController.NotificationSuppressionChangedListener listener) {
+            @Nullable final BubbleController.NotificationSuppressionChangedListener listener,
+            final BubbleController.PendingIntentCanceledListener intentCancelListener) {
         Objects.requireNonNull(e);
         mKey = e.getKey();
         mSuppressionListener = listener;
+        mIntentCancelListener = intent -> {
+            if (mIntent != null) {
+                mIntent.unregisterCancelListener(mIntentCancelListener);
+            }
+            intentCancelListener.onPendingIntentCanceled(this);
+        };
         setEntry(e);
     }
 
@@ -238,6 +262,10 @@
             mExpandedView = null;
         }
         mIconView = null;
+        if (mIntent != null) {
+            mIntent.unregisterCancelListener(mIntentCancelListener);
+        }
+        mIntentActive = false;
     }
 
     void setPendingIntentCanceled() {
@@ -371,11 +399,24 @@
             mDesiredHeight = entry.getBubbleMetadata().getDesiredHeight();
             mDesiredHeightResId = entry.getBubbleMetadata().getDesiredHeightResId();
             mIcon = entry.getBubbleMetadata().getIcon();
-            mIntent = entry.getBubbleMetadata().getIntent();
+
+            if (!mIntentActive || mIntent == null) {
+                if (mIntent != null) {
+                    mIntent.unregisterCancelListener(mIntentCancelListener);
+                }
+                mIntent = entry.getBubbleMetadata().getIntent();
+                if (mIntent != null) {
+                    mIntent.registerCancelListener(mIntentCancelListener);
+                }
+            } else if (mIntent != null && entry.getBubbleMetadata().getIntent() == null) {
+                // Was an intent bubble now it's a shortcut bubble... still unregister the listener
+                mIntent.unregisterCancelListener(mIntentCancelListener);
+                mIntent = null;
+            }
             mDeleteIntent = entry.getBubbleMetadata().getDeleteIntent();
         }
         mIsImportantConversation =
-                entry.getChannel() == null ? false : entry.getChannel().isImportantConversation();
+                entry.getChannel() != null && entry.getChannel().isImportantConversation();
     }
 
     @Nullable
@@ -395,10 +436,15 @@
     }
 
     /**
-     * @return if the bubble was ever expanded
+     * Sets if the intent used for this bubble is currently active (i.e. populating an
+     * expanded view, expanded or not).
      */
-    boolean getWasAccessed() {
-        return mLastAccessed != 0L;
+    void setIntentActive() {
+        mIntentActive = true;
+    }
+
+    boolean isIntentActive() {
+        return mIntentActive;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index bfed35e..1354585 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -263,6 +263,16 @@
     }
 
     /**
+     * Listener to be notified when a pending intent has been canceled for a bubble.
+     */
+    public interface PendingIntentCanceledListener {
+        /**
+         * Called when the pending intent for a bubble has been canceled.
+         */
+        void onPendingIntentCanceled(Bubble bubble);
+    }
+
+    /**
      * Callback for when the BubbleController wants to interact with the notification pipeline to:
      * - Remove a previously bubbled notification
      * - Update the notification shade since bubbled notification should/shouldn't be showing
@@ -390,6 +400,18 @@
                 }
             }
         });
+        mBubbleData.setPendingIntentCancelledListener(bubble -> {
+            if (bubble.getBubbleIntent() == null) {
+                return;
+            }
+            if (bubble.isIntentActive()) {
+                bubble.setPendingIntentCanceled();
+                return;
+            }
+            mHandler.post(
+                    () -> removeBubble(bubble.getKey(),
+                            BubbleController.DISMISS_INVALID_INTENT));
+        });
 
         mNotificationEntryManager = entryManager;
         mNotificationGroupManager = groupManager;
@@ -1102,23 +1124,7 @@
         // Lazy init stack view when a bubble is created
         ensureStackViewCreated();
         bubble.setInflateSynchronously(mInflateSynchronously);
-        bubble.inflate(
-                b -> {
-                    mBubbleData.notificationEntryUpdated(b, suppressFlyout,
-                            showInShade);
-                    if (bubble.getBubbleIntent() == null) {
-                        return;
-                    }
-                    bubble.getBubbleIntent().registerCancelListener(pendingIntent -> {
-                        if (bubble.getWasAccessed()) {
-                            bubble.setPendingIntentCanceled();
-                            return;
-                        }
-                        mHandler.post(
-                                () -> removeBubble(bubble.getKey(),
-                                        BubbleController.DISMISS_INVALID_INTENT));
-                    });
-                },
+        bubble.inflate(b -> mBubbleData.notificationEntryUpdated(b, suppressFlyout, showInShade),
                 mContext, mStackView, mBubbleIconFactory, false /* skipInflation */);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index ec4304f..d2dc506 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -59,7 +59,7 @@
 @Singleton
 public class BubbleData {
 
-    BubbleLogger mLogger = new BubbleLoggerImpl();
+    private BubbleLogger mLogger = new BubbleLoggerImpl();
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES;
 
@@ -137,6 +137,7 @@
 
     @Nullable
     private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
+    private BubbleController.PendingIntentCanceledListener mCancelledListener;
 
     /**
      * We track groups with summaries that aren't visibly displayed but still kept around because
@@ -167,6 +168,11 @@
         mSuppressionListener = listener;
     }
 
+    public void setPendingIntentCancelledListener(
+            BubbleController.PendingIntentCanceledListener listener) {
+        mCancelledListener = listener;
+    }
+
     public boolean hasBubbles() {
         return !mBubbles.isEmpty();
     }
@@ -236,7 +242,7 @@
                 bubbleToReturn = mPendingBubbles.get(key);
             } else if (entry != null) {
                 // New bubble
-                bubbleToReturn = new Bubble(entry, mSuppressionListener);
+                bubbleToReturn = new Bubble(entry, mSuppressionListener, mCancelledListener);
             } else {
                 // Persisted bubble being promoted
                 bubbleToReturn = persistedBubble;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 65bb89c..e26aa55 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -183,6 +183,9 @@
                                 // Apply flags to make behaviour match documentLaunchMode=always.
                                 fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
                                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+                                if (mBubble != null) {
+                                    mBubble.setIntentActive();
+                                }
                                 mActivityView.startActivity(mPendingIntent, fillInIntent, options);
                             }
                         } catch (RuntimeException e) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
index 1e99a7b..f683a63 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
@@ -312,6 +312,7 @@
 
     @Provides
     @Singleton
+    @Nullable
     static WifiManager provideWifiManager(Context context) {
         return context.getSystemService(WifiManager.class);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6b5c880..75f4809 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -589,7 +589,7 @@
             if (targetUserId != ActivityManager.getCurrentUser()) {
                 return;
             }
-
+            if (DEBUG) Log.d(TAG, "keyguardDone");
             tryKeyguardDone();
         }
 
@@ -608,6 +608,7 @@
         @Override
         public void keyguardDonePending(boolean strongAuth, int targetUserId) {
             Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
+            if (DEBUG) Log.d(TAG, "keyguardDonePending");
             if (targetUserId != ActivityManager.getCurrentUser()) {
                 Trace.endSection();
                 return;
@@ -626,6 +627,7 @@
         @Override
         public void keyguardGone() {
             Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardGone");
+            if (DEBUG) Log.d(TAG, "keyguardGone");
             mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false);
             mKeyguardDisplayManager.hide();
             Trace.endSection();
@@ -1690,9 +1692,14 @@
     };
 
     private void tryKeyguardDone() {
+        if (DEBUG) {
+            Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+                    + mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
+        }
         if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
             handleKeyguardDone();
         } else if (!mHideAnimationRun) {
+            if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
             mHideAnimationRun = true;
             mHideAnimationRunning = true;
             mKeyguardViewControllerLazy.get()
@@ -1919,6 +1926,7 @@
     };
 
     private final Runnable mHideAnimationFinishedRunnable = () -> {
+        Log.e(TAG, "mHideAnimationFinishedRunnable#run");
         mHideAnimationRunning = false;
         tryKeyguardDone();
     };
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
index 0904ebc..e8f0e06 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataCombineLatest.kt
@@ -62,9 +62,9 @@
      */
     fun getData(): Map<String, MediaData> {
         return entries.filter {
-            (key, pair) -> pair.first != null
+            (key, pair) -> pair.first != null && pair.second != null
         }.mapValues {
-            (key, pair) -> pair.first!!
+            (key, pair) -> pair.first!!.copy(device = pair.second)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 3d2b72d..fc33391 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -40,6 +40,28 @@
 import javax.inject.Singleton
 
 /**
+ * Similarly to isShown but also excludes views that have 0 alpha
+ */
+val View.isShownNotFaded: Boolean
+    get() {
+        var current: View = this
+        while (true) {
+            if (current.visibility != View.VISIBLE) {
+                return false
+            }
+            if (current.alpha == 0.0f) {
+                return false
+            }
+            val parent = current.parent ?: return false // We are not attached to the view root
+            if (parent !is View) {
+                // we reached the viewroot, hurray
+                return true
+            }
+            current = parent
+        }
+    }
+
+/**
  * This manager is responsible for placement of the unique media view between the different hosts
  * and animate the positions of the views to achieve seamless transitions.
  */
@@ -368,7 +390,7 @@
             // non-trivial reattaching logic happening that will make the view not-shown earlier
             return true
         }
-        return mediaFrame.isShown || animator.isRunning || animationPending
+        return mediaFrame.isShownNotFaded || animator.isRunning || animationPending
     }
 
     private fun adjustAnimatorForTransition(desiredLocation: Int, previousLocation: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 51eca67..afc5be4e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -265,9 +265,13 @@
 
     private void createDialog() {
         final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
+        boolean isProfileOwnerOfOrganizationOwnedDevice =
+                mSecurityController.isProfileOwnerOfOrganizationOwnedDevice();
         final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
         final CharSequence deviceOwnerOrganization =
                 mSecurityController.getDeviceOwnerOrganizationName();
+        final CharSequence workProfileOrganizationName =
+                mSecurityController.getWorkProfileOrganizationName();
         final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
         final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
         final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
@@ -284,7 +288,8 @@
 
         // device management section
         CharSequence managementMessage = getManagementMessage(isDeviceManaged,
-                deviceOwnerOrganization);
+                deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
+                workProfileOrganizationName);
         if (managementMessage == null) {
             dialogView.findViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
         } else {
@@ -292,7 +297,11 @@
             TextView deviceManagementWarning =
                     (TextView) dialogView.findViewById(R.id.device_management_warning);
             deviceManagementWarning.setText(managementMessage);
-            mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
+            // Don't show the policies button for profile owner of org owned device, because there
+            // is no policies settings screen for it
+            if (!isProfileOwnerOfOrganizationOwnedDevice) {
+                mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
+            }
         }
 
         // ca certificate section
@@ -382,11 +391,18 @@
     }
 
     protected CharSequence getManagementMessage(boolean isDeviceManaged,
-            CharSequence organizationName) {
-        if (!isDeviceManaged) return null;
-        if (organizationName != null)
+            CharSequence organizationName, boolean isProfileOwnerOfOrganizationOwnedDevice,
+            CharSequence workProfileOrganizationName) {
+        if (!isDeviceManaged && !isProfileOwnerOfOrganizationOwnedDevice) {
+            return null;
+        }
+        if (isDeviceManaged && organizationName != null) {
             return mContext.getString(
                     R.string.monitoring_description_named_management, organizationName);
+        } else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
+            return mContext.getString(
+                    R.string.monitoring_description_named_management, workProfileOrganizationName);
+        }
         return mContext.getString(R.string.monitoring_description_management);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index b5afe77..b07b1a9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -44,6 +44,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -146,6 +147,7 @@
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
 
     private boolean mHasTopCutout = false;
+    private int mStatusBarPaddingTop = 0;
     private int mRoundedCornerPadding = 0;
     private int mContentMarginStart;
     private int mContentMarginEnd;
@@ -339,6 +341,7 @@
 
         mRoundedCornerPadding = resources.getDimensionPixelSize(
                 R.dimen.rounded_corner_content_padding);
+        mStatusBarPaddingTop = resources.getDimensionPixelSize(R.dimen.status_bar_padding_top);
 
         // Update height for a few views, especially due to landscape mode restricting space.
         mHeaderTextContainerView.getLayoutParams().height =
@@ -460,6 +463,11 @@
     private void updateClockPadding() {
         int clockPaddingLeft = 0;
         int clockPaddingRight = 0;
+
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+        int leftMargin = lp.leftMargin;
+        int rightMargin = lp.rightMargin;
+
         // The clock might collide with cutouts, let's shift it out of the way.
         // We only do that if the inset is bigger than our own padding, since it's nicer to
         // align with
@@ -467,16 +475,19 @@
             // if there's a cutout, let's use at least the rounded corner inset
             int cutoutPadding = Math.max(mCutOutPaddingLeft, mRoundedCornerPadding);
             int contentMarginLeft = isLayoutRtl() ? mContentMarginEnd : mContentMarginStart;
-            clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft, 0);
+            clockPaddingLeft = Math.max(cutoutPadding - contentMarginLeft - leftMargin, 0);
         }
         if (mCutOutPaddingRight > 0) {
             // if there's a cutout, let's use at least the rounded corner inset
             int cutoutPadding = Math.max(mCutOutPaddingRight, mRoundedCornerPadding);
             int contentMarginRight = isLayoutRtl() ? mContentMarginStart : mContentMarginEnd;
-            clockPaddingRight = Math.max(cutoutPadding - contentMarginRight, 0);
+            clockPaddingRight = Math.max(cutoutPadding - contentMarginRight - rightMargin, 0);
         }
 
-        mSystemIconsView.setPadding(clockPaddingLeft, mWaterfallTopInset, clockPaddingRight, 0);
+        mSystemIconsView.setPadding(clockPaddingLeft,
+                mWaterfallTopInset + mStatusBarPaddingTop,
+                clockPaddingRight,
+                0);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index d6e1a16..8544430 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -214,9 +214,9 @@
     private Animator mScreenshotAnimation;
     private Runnable mOnCompleteRunnable;
     private Animator mDismissAnimation;
-    private boolean mInDarkMode = false;
-    private boolean mDirectionLTR = true;
-    private boolean mOrientationPortrait = true;
+    private boolean mInDarkMode;
+    private boolean mDirectionLTR;
+    private boolean mOrientationPortrait;
 
     private float mCornerSizeX;
     private float mDismissDeltaY;
@@ -245,9 +245,6 @@
         }
     };
 
-    /**
-     * @param context everything needs a context :(
-     */
     @Inject
     public GlobalScreenshot(
             Context context, @Main Resources resources,
@@ -320,6 +317,104 @@
         inoutInfo.touchableRegion.set(touchRegion);
     }
 
+    void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
+        mOnCompleteRunnable = onComplete;
+
+        mDisplay.getRealMetrics(mDisplayMetrics);
+        takeScreenshotInternal(
+                finisher,
+                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
+    }
+
+    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
+            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
+            Consumer<Uri> finisher, Runnable onComplete) {
+        // TODO: use task Id, userId, topComponent for smart handler
+
+        mOnCompleteRunnable = onComplete;
+        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
+            saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
+        } else {
+            saveScreenshot(screenshot, finisher,
+                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
+                    true);
+        }
+    }
+
+    /**
+     * Displays a screenshot selector
+     */
+    @SuppressLint("ClickableViewAccessibility")
+    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
+        dismissScreenshot("new screenshot requested", true);
+        mOnCompleteRunnable = onComplete;
+
+        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
+        mScreenshotSelectorView.setOnTouchListener((v, event) -> {
+            ScreenshotSelectorView view = (ScreenshotSelectorView) v;
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    view.startSelection((int) event.getX(), (int) event.getY());
+                    return true;
+                case MotionEvent.ACTION_MOVE:
+                    view.updateSelection((int) event.getX(), (int) event.getY());
+                    return true;
+                case MotionEvent.ACTION_UP:
+                    view.setVisibility(View.GONE);
+                    mWindowManager.removeView(mScreenshotLayout);
+                    final Rect rect = view.getSelectionRect();
+                    if (rect != null) {
+                        if (rect.width() != 0 && rect.height() != 0) {
+                            // Need mScreenshotLayout to handle it after the view disappears
+                            mScreenshotLayout.post(() -> takeScreenshotInternal(finisher, rect));
+                        }
+                    }
+
+                    view.stopSelection();
+                    return true;
+            }
+
+            return false;
+        });
+        mScreenshotLayout.post(() -> {
+            mScreenshotSelectorView.setVisibility(View.VISIBLE);
+            mScreenshotSelectorView.requestFocus();
+        });
+    }
+
+    /**
+     * Cancels screenshot request
+     */
+    void stopScreenshot() {
+        // If the selector layer still presents on screen, we remove it and resets its state.
+        if (mScreenshotSelectorView.getSelectionRect() != null) {
+            mWindowManager.removeView(mScreenshotLayout);
+            mScreenshotSelectorView.stopSelection();
+        }
+    }
+
+    /**
+     * Clears current screenshot
+     */
+    void dismissScreenshot(String reason, boolean immediate) {
+        Log.v(TAG, "clearing screenshot: " + reason);
+        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+        mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+        if (!immediate) {
+            mDismissAnimation = createScreenshotDismissAnimation();
+            mDismissAnimation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    clearScreenshot();
+                }
+            });
+            mDismissAnimation.start();
+        } else {
+            clearScreenshot();
+        }
+    }
+
     private void onConfigChanged(Configuration newConfig) {
         boolean needsUpdate = false;
         // dark mode
@@ -408,15 +503,12 @@
             }
             return mScreenshotLayout.onApplyWindowInsets(insets);
         });
-        mScreenshotLayout.setOnKeyListener(new View.OnKeyListener() {
-            @Override
-            public boolean onKey(View v, int keyCode, KeyEvent event) {
-                if (keyCode == KeyEvent.KEYCODE_BACK) {
-                    dismissScreenshot("back pressed", true);
-                    return true;
-                }
-                return false;
+        mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
+            if (keyCode == KeyEvent.KEYCODE_BACK) {
+                dismissScreenshot("back pressed", true);
+                return true;
             }
+            return false;
         });
         // Get focus so that the key events go to the layout.
         mScreenshotLayout.setFocusableInTouchMode(true);
@@ -471,59 +563,19 @@
     }
 
     /**
-     * Updates the window focusability.  If the window is already showing, then it updates the
-     * window immediately, otherwise the layout params will be applied when the window is next
-     * shown.
-     */
-    private void setWindowFocusable(boolean focusable) {
-        if (focusable) {
-            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
-        } else {
-            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
-        }
-        if (mScreenshotLayout.isAttachedToWindow()) {
-            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
-        }
-    }
-
-    /**
-     * Creates a new worker thread and saves the screenshot to the media store.
-     */
-    private void saveScreenshotInWorkerThread(
-            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
-        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
-        data.image = mScreenBitmap;
-        data.finisher = finisher;
-        data.mActionsReadyListener = actionsReadyListener;
-
-        if (mSaveInBgTask != null) {
-            // just log success/failure for the pre-existing screenshot
-            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
-                @Override
-                void onActionsReady(SavedImageData imageData) {
-                    logSuccessOnActionsReady(imageData);
-                }
-            });
-        }
-
-        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
-        mSaveInBgTask.execute();
-    }
-
-    /**
      * Takes a screenshot of the current display and shows an animation.
      */
-    private void takeScreenshot(Consumer<Uri> finisher, Rect crop) {
+    private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
         // copy the input Rect, since SurfaceControl.screenshot can mutate it
         Rect screenRect = new Rect(crop);
         int rot = mDisplay.getRotation();
         int width = crop.width();
         int height = crop.height();
-        takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
+        saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
                 Insets.NONE, true);
     }
 
-    private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
+    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, boolean showFlash) {
         dismissScreenshot("new screenshot requested", true);
 
@@ -561,85 +613,6 @@
         startAnimation(finisher, screenRect, screenInsets, showFlash);
     }
 
-    void takeScreenshot(Consumer<Uri> finisher, Runnable onComplete) {
-        mOnCompleteRunnable = onComplete;
-
-        mDisplay.getRealMetrics(mDisplayMetrics);
-        takeScreenshot(
-                finisher,
-                new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
-    }
-
-    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
-            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
-            Consumer<Uri> finisher, Runnable onComplete) {
-        // TODO: use task Id, userId, topComponent for smart handler
-
-        mOnCompleteRunnable = onComplete;
-        if (aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
-            takeScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, false);
-        } else {
-            takeScreenshot(screenshot, finisher,
-                    new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight()), Insets.NONE,
-                    true);
-        }
-    }
-
-    /**
-     * Displays a screenshot selector
-     */
-    @SuppressLint("ClickableViewAccessibility")
-    void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
-        dismissScreenshot("new screenshot requested", true);
-        mOnCompleteRunnable = onComplete;
-
-        mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-        mScreenshotSelectorView.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                ScreenshotSelectorView view = (ScreenshotSelectorView) v;
-                switch (event.getAction()) {
-                    case MotionEvent.ACTION_DOWN:
-                        view.startSelection((int) event.getX(), (int) event.getY());
-                        return true;
-                    case MotionEvent.ACTION_MOVE:
-                        view.updateSelection((int) event.getX(), (int) event.getY());
-                        return true;
-                    case MotionEvent.ACTION_UP:
-                        view.setVisibility(View.GONE);
-                        mWindowManager.removeView(mScreenshotLayout);
-                        final Rect rect = view.getSelectionRect();
-                        if (rect != null) {
-                            if (rect.width() != 0 && rect.height() != 0) {
-                                // Need mScreenshotLayout to handle it after the view disappears
-                                mScreenshotLayout.post(() -> takeScreenshot(finisher, rect));
-                            }
-                        }
-
-                        view.stopSelection();
-                        return true;
-                }
-
-                return false;
-            }
-        });
-        mScreenshotLayout.post(() -> {
-            mScreenshotSelectorView.setVisibility(View.VISIBLE);
-            mScreenshotSelectorView.requestFocus();
-        });
-    }
-
-    /**
-     * Cancels screenshot request
-     */
-    void stopScreenshot() {
-        // If the selector layer still presents on screen, we remove it and resets its state.
-        if (mScreenshotSelectorView.getSelectionRect() != null) {
-            mWindowManager.removeView(mScreenshotLayout);
-            mScreenshotSelectorView.stopSelection();
-        }
-    }
-
     /**
      * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
      * failure).
@@ -670,104 +643,6 @@
         });
     }
 
-    private boolean isUserSetupComplete() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
-    }
-
-    /**
-     * Clears current screenshot
-     */
-    void dismissScreenshot(String reason, boolean immediate) {
-        Log.v(TAG, "clearing screenshot: " + reason);
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
-        mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
-        if (!immediate) {
-            mDismissAnimation = createScreenshotDismissAnimation();
-            mDismissAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    clearScreenshot();
-                }
-            });
-            mDismissAnimation.start();
-        } else {
-            clearScreenshot();
-        }
-    }
-
-    private void clearScreenshot() {
-        if (mScreenshotLayout.isAttachedToWindow()) {
-            mWindowManager.removeView(mScreenshotLayout);
-        }
-
-        // Clear any references to the bitmap
-        mScreenshotPreview.setImageDrawable(null);
-        mScreenshotAnimatedView.setImageDrawable(null);
-        mScreenshotAnimatedView.setVisibility(View.GONE);
-        mActionsContainerBackground.setVisibility(View.GONE);
-        mActionsContainer.setVisibility(View.GONE);
-        mBackgroundProtection.setAlpha(0f);
-        mDismissButton.setVisibility(View.GONE);
-        mScreenshotPreview.setVisibility(View.GONE);
-        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
-        mScreenshotPreview.setContentDescription(
-                mContext.getResources().getString(R.string.screenshot_preview_description));
-        mScreenshotLayout.setAlpha(1);
-        mDismissButton.setTranslationY(0);
-        mActionsContainer.setTranslationY(0);
-        mActionsContainerBackground.setTranslationY(0);
-        mScreenshotPreview.setTranslationY(0);
-    }
-
-    /**
-     * Sets up the action shade and its entrance animation, once we get the screenshot URI.
-     */
-    private void showUiOnActionsReady(SavedImageData imageData) {
-        logSuccessOnActionsReady(imageData);
-
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
-                SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
-                AccessibilityManager.FLAG_CONTENT_CONTROLS);
-
-        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
-        mScreenshotHandler.sendMessageDelayed(
-                mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
-                timeoutMs);
-
-        if (imageData.uri != null) {
-            mScreenshotHandler.post(() -> {
-                if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
-                    mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            super.onAnimationEnd(animation);
-                            createScreenshotActionsShadeAnimation(imageData).start();
-                        }
-                    });
-                } else {
-                    createScreenshotActionsShadeAnimation(imageData).start();
-                }
-            });
-        }
-    }
-
-    /**
-     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
-     */
-    private void logSuccessOnActionsReady(SavedImageData imageData) {
-        if (imageData.uri == null) {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-        } else {
-            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
-        }
-    }
-
     /**
      * Starts the animation after taking the screenshot
      */
@@ -818,6 +693,77 @@
         });
     }
 
+    /**
+     * Creates a new worker thread and saves the screenshot to the media store.
+     */
+    private void saveScreenshotInWorkerThread(
+            Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
+        SaveImageInBackgroundData data = new SaveImageInBackgroundData();
+        data.image = mScreenBitmap;
+        data.finisher = finisher;
+        data.mActionsReadyListener = actionsReadyListener;
+
+        if (mSaveInBgTask != null) {
+            // just log success/failure for the pre-existing screenshot
+            mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
+                @Override
+                void onActionsReady(SavedImageData imageData) {
+                    logSuccessOnActionsReady(imageData);
+                }
+            });
+        }
+
+        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data);
+        mSaveInBgTask.execute();
+    }
+
+    /**
+     * Sets up the action shade and its entrance animation, once we get the screenshot URI.
+     */
+    private void showUiOnActionsReady(SavedImageData imageData) {
+        logSuccessOnActionsReady(imageData);
+
+        AccessibilityManager accessibilityManager = (AccessibilityManager)
+                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        long timeoutMs = accessibilityManager.getRecommendedTimeoutMillis(
+                SCREENSHOT_CORNER_DEFAULT_TIMEOUT_MILLIS,
+                AccessibilityManager.FLAG_CONTENT_CONTROLS);
+
+        mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+        mScreenshotHandler.sendMessageDelayed(
+                mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
+                timeoutMs);
+
+        if (imageData.uri != null) {
+            mScreenshotHandler.post(() -> {
+                if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+                    mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            super.onAnimationEnd(animation);
+                            createScreenshotActionsShadeAnimation(imageData).start();
+                        }
+                    });
+                } else {
+                    createScreenshotActionsShadeAnimation(imageData).start();
+                }
+            });
+        }
+    }
+
+    /**
+     * Logs success/failure of the screenshot saving task, and shows an error if it failed.
+     */
+    private void logSuccessOnActionsReady(SavedImageData imageData) {
+        if (imageData.uri == null) {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+        } else {
+            mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+        }
+    }
+
     private AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
         Rect previewBounds = new Rect();
         mScreenshotPreview.getBoundsOnScreen(previewBounds);
@@ -1070,6 +1016,30 @@
         return animSet;
     }
 
+    private void clearScreenshot() {
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            mWindowManager.removeView(mScreenshotLayout);
+        }
+
+        // Clear any references to the bitmap
+        mScreenshotPreview.setImageDrawable(null);
+        mScreenshotAnimatedView.setImageDrawable(null);
+        mScreenshotAnimatedView.setVisibility(View.GONE);
+        mActionsContainerBackground.setVisibility(View.GONE);
+        mActionsContainer.setVisibility(View.GONE);
+        mBackgroundProtection.setAlpha(0f);
+        mDismissButton.setVisibility(View.GONE);
+        mScreenshotPreview.setVisibility(View.GONE);
+        mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
+        mScreenshotPreview.setContentDescription(
+                mContext.getResources().getString(R.string.screenshot_preview_description));
+        mScreenshotLayout.setAlpha(1);
+        mDismissButton.setTranslationY(0);
+        mActionsContainer.setTranslationY(0);
+        mActionsContainerBackground.setTranslationY(0);
+        mScreenshotPreview.setTranslationY(0);
+    }
+
     private void setAnimatedViewSize(int width, int height) {
         ViewGroup.LayoutParams layoutParams = mScreenshotAnimatedView.getLayoutParams();
         layoutParams.width = width;
@@ -1077,6 +1047,27 @@
         mScreenshotAnimatedView.setLayoutParams(layoutParams);
     }
 
+    /**
+     * Updates the window focusability.  If the window is already showing, then it updates the
+     * window immediately, otherwise the layout params will be applied when the window is next
+     * shown.
+     */
+    private void setWindowFocusable(boolean focusable) {
+        if (focusable) {
+            mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE;
+        } else {
+            mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
+        }
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams);
+        }
+    }
+
+    private boolean isUserSetupComplete() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+    }
+
     /** Does the aspect ratio of the bitmap with insets removed match the bounds. */
     private boolean aspectRatiosMatch(Bitmap bitmap, Insets bitmapInsets, Rect screenBounds) {
         int insettedWidth = bitmap.getWidth() - bitmapInsets.left - bitmapInsets.right;
@@ -1128,7 +1119,7 @@
         if (insets.left < 0 || insets.top < 0 || insets.right < 0 || insets.bottom < 0) {
             // Are any of the insets negative, meaning the bitmap is smaller than the bounds so need
             // to fill in the background of the drawable.
-            return new LayerDrawable(new Drawable[] {
+            return new LayerDrawable(new Drawable[]{
                     new ColorDrawable(Color.BLACK), insetDrawable});
         } else {
             return insetDrawable;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 9f8a9bb..6f143da 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -102,7 +102,7 @@
 
             switch (msg.what) {
                 case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
-                    mScreenshot.takeScreenshot(uriConsumer, onComplete);
+                    mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
                     break;
                 case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
                     mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 39949c8..b6a284c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -26,7 +26,6 @@
 import android.os.UserManager;
 import android.util.Log;
 import android.util.MathUtils;
-import android.util.Slog;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -169,7 +168,7 @@
 
         // This condition may indicate an error on Android, so log it.
         if (!allowDismissKeyguard) {
-            Slog.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
+            Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
         }
 
         mShowingSoon = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index da1ef2f..6106f38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.systemui.R;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.util.Utils;
 
 import java.io.FileDescriptor;
@@ -68,7 +69,7 @@
     private MediaProjectionInfo mProjection;
 
     @Inject
-    public CastControllerImpl(Context context) {
+    public CastControllerImpl(Context context, DumpManager dumpManager) {
         mContext = context;
         mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
         mMediaRouter.setRouterGroupId(MediaRouter.MIRRORING_GROUP_ID);
@@ -76,6 +77,7 @@
                 context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);
         mProjection = mProjectionManager.getActiveProjectionInfo();
         mProjectionManager.addCallback(mProjectionCallback, new Handler());
+        dumpManager.registerDumpable(TAG, this);
         if (DEBUG) Log.d(TAG, "new CastController()");
     }
 
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 3c0bee8..32c4aec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -24,6 +24,7 @@
 import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
 import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE;
 
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -175,7 +176,7 @@
     public NetworkControllerImpl(Context context, @Background Looper bgLooper,
             DeviceProvisionedController deviceProvisionedController,
             BroadcastDispatcher broadcastDispatcher, ConnectivityManager connectivityManager,
-            TelephonyManager telephonyManager, WifiManager wifiManager,
+            TelephonyManager telephonyManager, @Nullable WifiManager wifiManager,
             NetworkScoreManager networkScoreManager) {
         this(context, connectivityManager,
                 telephonyManager,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 9e056cf..c36bb04 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -423,7 +423,7 @@
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
         mTestableLooper.processAllMessages();
 
-        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
         verify(mFaceManager).isHardwareDetected();
         verify(mFaceManager).hasEnrolledTemplates(anyInt());
     }
@@ -433,7 +433,7 @@
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -443,7 +443,7 @@
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -455,7 +455,7 @@
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -478,14 +478,14 @@
         mKeyguardUpdateMonitor.dispatchStartedWakingUp();
         mTestableLooper.processAllMessages();
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
 
         // Stop scanning when bouncer becomes visible
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true /* showingBouncer */);
         mTestableLooper.processAllMessages();
         clearInvocations(mFaceManager);
         mKeyguardUpdateMonitor.requestFaceAuth();
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -493,7 +493,7 @@
         mKeyguardUpdateMonitor.setKeyguardOccluded(true);
         mKeyguardUpdateMonitor.setAssistantVisible(true);
 
-        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -505,7 +505,7 @@
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
                 KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -515,7 +515,7 @@
         mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
                 KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */);
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -526,7 +526,7 @@
                 KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
 
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -537,7 +537,7 @@
                 KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT);
 
         mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
-        verify(mFaceManager).authenticate(any(), any(), anyInt(), any(), any(), anyInt());
+        verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt());
     }
 
     @Test
@@ -549,7 +549,7 @@
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
         mTestableLooper.processAllMessages();
 
-        verify(mFaceManager, never()).authenticate(any(), any(), anyInt(), any(), any());
+        verify(mFaceManager, never()).authenticate(any(), any(), any(), any());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
index ed4e686..315caee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleDataTest.java
@@ -107,6 +107,9 @@
     @Mock
     private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
 
+    @Mock
+    private BubbleController.PendingIntentCanceledListener mPendingIntentCanceledListener;
+
     @Before
     public void setUp() throws Exception {
         mNotificationTestHelper = new NotificationTestHelper(
@@ -127,20 +130,20 @@
         modifyRanking(mEntryInterruptive)
                 .setVisuallyInterruptive(true)
                 .build();
-        mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener);
+        mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener, null);
 
         ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
         mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
         mEntryDismissed.setRow(row);
-        mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener);
+        mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener, null);
 
-        mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener);
-        mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener);
-        mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener);
-        mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener);
-        mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener);
-        mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener);
-        mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener);
+        mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener, mPendingIntentCanceledListener);
+        mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener, mPendingIntentCanceledListener);
+        mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener, mPendingIntentCanceledListener);
+        mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener, mPendingIntentCanceledListener);
+        mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener, mPendingIntentCanceledListener);
+        mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener, mPendingIntentCanceledListener);
+        mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener, mPendingIntentCanceledListener);
 
         mBubbleData = new BubbleData(getContext());
 
@@ -847,14 +850,6 @@
         when(entry.getSbn().getPostTime()).thenReturn(postTime);
     }
 
-    private void setOngoing(NotificationEntry entry, boolean ongoing) {
-        if (ongoing) {
-            entry.getSbn().getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
-        } else {
-            entry.getSbn().getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
-        }
-    }
-
     /**
      * No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
      * required for BubbleData functionality and verification. NotificationTestHelper is used only
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
index be03923..2bcc22c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleTest.java
@@ -71,7 +71,7 @@
                 .setNotification(mNotif)
                 .build();
 
-        mBubble = new Bubble(mEntry, mSuppressionListener);
+        mBubble = new Bubble(mEntry, mSuppressionListener, null);
 
         Intent target = new Intent(mContext, BubblesTestActivity.class);
         Notification.BubbleMetadata metadata = new Notification.BubbleMetadata.Builder(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
index 5d4693d..dbc5596 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java
@@ -39,6 +39,7 @@
 import org.mockito.Mock;
 
 import java.util.ArrayList;
+import java.util.Map;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -159,6 +160,18 @@
         verify(mListener).onMediaDataLoaded(eq("NEW_KEY"), any(), captor.capture());
     }
 
+    @Test
+    public void getDataIncludesDevice() {
+        // GIVEN that device and media events have been received
+        mDeviceListener.onMediaDeviceChanged(KEY, mDeviceData);
+        mDataListener.onMediaDataLoaded(KEY, null, mMediaData);
+
+        // THEN the result of getData includes device info
+        Map<String, MediaData> results = mManager.getData();
+        assertThat(results.get(KEY)).isNotNull();
+        assertThat(results.get(KEY).getDevice()).isEqualTo(mDeviceData);
+    }
+
     private MediaDataManager.Listener captureDataListener() {
         ArgumentCaptor<MediaDataManager.Listener> captor = ArgumentCaptor.forClass(
                 MediaDataManager.Listener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index ea5449b..417b19f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -367,13 +367,46 @@
     }
 
     @Test
-    public void testGetManagementMessage() {
-        assertEquals(null, mFooter.getManagementMessage(false, MANAGING_ORGANIZATION));
+    public void testGetManagementMessage_noManagement() {
+        assertEquals(null, mFooter.getManagementMessage(
+                /* isDeviceManaged= */ false,
+                MANAGING_ORGANIZATION,
+                /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+                MANAGING_ORGANIZATION));
+    }
+
+    @Test
+    public void testGetManagementMessage_deviceOwner() {
         assertEquals(mContext.getString(R.string.monitoring_description_named_management,
                                         MANAGING_ORGANIZATION),
-                     mFooter.getManagementMessage(true, MANAGING_ORGANIZATION));
+                     mFooter.getManagementMessage(
+                             /* isDeviceManaged= */ true,
+                             MANAGING_ORGANIZATION,
+                             /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+                             /* workProfileOrganizationName= */ null));
         assertEquals(mContext.getString(R.string.monitoring_description_management),
-                     mFooter.getManagementMessage(true, null));
+                     mFooter.getManagementMessage(
+                             /* isDeviceManaged= */ true,
+                             /* organizationName= */ null,
+                             /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+                             /* workProfileOrganizationName= */ null));
+    }
+
+    @Test
+    public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() {
+        assertEquals(mContext.getString(R.string.monitoring_description_named_management,
+                MANAGING_ORGANIZATION),
+                mFooter.getManagementMessage(
+                        /* isDeviceManaged= */ false,
+                        /* organizationName= */ null,
+                        /* isProfileOwnerOfOrganizationOwnedDevice= */ true,
+                        MANAGING_ORGANIZATION));
+        assertEquals(mContext.getString(R.string.monitoring_description_management),
+                mFooter.getManagementMessage(
+                        /* isDeviceManaged= */ false,
+                        /* organizationName= */ null,
+                        /* isProfileOwnerOfOrganizationOwnedDevice= */ true,
+                        /* workProfileOrganizationName= */ null));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
index 016e402..db50163 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CastControllerImplTest.java
@@ -17,6 +17,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 import com.android.systemui.statusbar.policy.CastController.Callback;
 
 import org.junit.Before;
@@ -51,7 +52,7 @@
         mContext.addMockSystemService(MediaProjectionManager.class, mMediaProjectionManager);
         when(mMediaProjectionManager.getActiveProjectionInfo()).thenReturn(mProjection);
 
-        mController = new CastControllerImpl(mContext);
+        mController = new CastControllerImpl(mContext, mock(DumpManager.class));
     }
 
     @Test
diff --git a/packages/Tethering/jarjar-rules.txt b/packages/Tethering/jarjar-rules.txt
index 2d3108a..591861f 100644
--- a/packages/Tethering/jarjar-rules.txt
+++ b/packages/Tethering/jarjar-rules.txt
@@ -3,7 +3,7 @@
 # If there are files in that filegroup that are not covered below, the classes in the
 # module will be overwritten by the ones in the framework.
 rule com.android.internal.util.** com.android.networkstack.tethering.util.@1
-rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
+rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
 
 rule android.net.shared.Inet4AddressUtils* com.android.networkstack.tethering.shared.Inet4AddressUtils@1
 
diff --git a/packages/Tethering/tests/unit/jarjar-rules.txt b/packages/Tethering/tests/unit/jarjar-rules.txt
index 1ea56cd..ec2d2b0 100644
--- a/packages/Tethering/tests/unit/jarjar-rules.txt
+++ b/packages/Tethering/tests/unit/jarjar-rules.txt
@@ -8,4 +8,4 @@
 rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
 rule com.android.internal.util.TrafficStatsConstants* com.android.networkstack.tethering.util.TrafficStatsConstants@1
 
-rule android.net.LocalLog* com.android.networkstack.tethering.LocalLog@1
+rule android.util.LocalLog* com.android.networkstack.tethering.util.LocalLog@1
diff --git a/packages/services/PacProcessor/AndroidManifest.xml b/packages/services/PacProcessor/AndroidManifest.xml
index 6740c16..ad13261 100644
--- a/packages/services/PacProcessor/AndroidManifest.xml
+++ b/packages/services/PacProcessor/AndroidManifest.xml
@@ -5,7 +5,9 @@
     <uses-permission android:name="android.permission.INTERNET" />
 
     <application
-        android:label="@string/app_name">
+        android:label="@string/app_name"
+        android:defaultToDeviceProtectedStorage="true"
+        android:directBootAware="true">
 
         <service android:name=".PacService"
             android:exported="true">
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index a734df7..5c3f2d8 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -18,10 +18,10 @@
     libjnigraphics
 
 LOCAL_HEADER_LIBRARIES := \
+    jni_headers \
     libbase_headers
 
 LOCAL_C_INCLUDES += \
-    $(JNI_H_INCLUDE) \
     frameworks/rs
 
 LOCAL_CFLAGS += -Wno-unused-parameter
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 7241ad3..64d0e91 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -119,7 +119,7 @@
         "android.hardware.tv.cec-V1.0-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.1-java",
-        "android.hardware.biometrics.fingerprint-V2.2-java",
+        "android.hardware.biometrics.fingerprint-V2.3-java",
         "android.hardware.oemlock-V1.0-java",
         "android.hardware.configstore-V1.0-java",
         "android.hardware.contexthub-V1.0-java",
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 766e36d..7334c86 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -4737,7 +4737,7 @@
             return true;
         }
 
-        private void killAppForOpChange(int code, int uid, String packageName) {
+        private void killAppForOpChange(int code, int uid) {
             final IActivityManager am = ActivityManager.getService();
             try {
                 am.killUid(UserHandle.getAppId(uid), UserHandle.USER_ALL,
@@ -4754,7 +4754,7 @@
                     switch(code) {
                         case OP_REQUEST_INSTALL_PACKAGES:
                             // Always kill regardless of op change, to remount apps /storage
-                            killAppForOpChange(code, uid, packageName);
+                            killAppForOpChange(code, uid);
                             return;
                         case OP_MANAGE_EXTERNAL_STORAGE:
                             if (mode != MODE_ALLOWED) {
@@ -4763,12 +4763,7 @@
                                 // results in a bad UX, especially since the gid only gives access
                                 // to unreliable volumes, USB OTGs that are rarely mounted. The app
                                 // will get the external_storage gid on next organic restart.
-                                if (packageName != null) {
-                                    killAppForOpChange(code, uid, packageName);
-                                } else {
-                                    // TODO(b/158283222) this can happen, figure out if we need
-                                    // to kill in this case as well.
-                                }
+                                killAppForOpChange(code, uid);
                             }
                             return;
                         case OP_LEGACY_STORAGE:
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 135fe04..3d1691d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2642,6 +2642,8 @@
             shouldCheckLocationPermissions = true;
         }
 
+        boolean isPermissionCheckSuccessful = true;
+
         if (shouldCheckLocationPermissions) {
             LocationAccessPolicy.LocationPermissionResult result =
                     LocationAccessPolicy.checkLocationPermission(
@@ -2651,14 +2653,14 @@
                     throw new SecurityException("Unable to listen for events " + events + " due to "
                             + "insufficient location permissions.");
                 case DENIED_SOFT:
-                    return false;
+                    isPermissionCheckSuccessful = false;
             }
         }
 
         if ((events & ENFORCE_PHONE_STATE_PERMISSION_MASK) != 0) {
             if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
                     mContext, subId, callingPackage, callingFeatureId, message)) {
-                return false;
+                isPermissionCheckSuccessful = false;
             }
         }
 
@@ -2688,7 +2690,7 @@
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
         }
 
-        return true;
+        return isPermissionCheckSuccessful;
     }
 
     private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a1ea4b7..90f87ce 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -236,7 +236,6 @@
 import android.media.audiofx.AudioEffect;
 import android.net.Proxy;
 import android.net.Uri;
-import android.os.AndroidTimeoutException;
 import android.os.AppZygote;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -2887,8 +2886,7 @@
             // log all others.
             if (!(e instanceof SecurityException
                     || e instanceof IllegalArgumentException
-                    || e instanceof IllegalStateException
-                    || e instanceof AndroidTimeoutException)) {
+                    || e instanceof IllegalStateException)) {
                 Slog.wtf(TAG, "Activity Manager Crash."
                         + " UID:" + Binder.getCallingUid()
                         + " PID:" + Binder.getCallingPid()
@@ -7464,11 +7462,6 @@
                     + name
                     + " providerRunning=" + providerRunning
                     + " caller=" + callerName + "/" + Binder.getCallingUid());
-
-            final int clientTargetSdk = mPackageManagerInt.getUidTargetSdkVersion(callingUid);
-            if (clientTargetSdk >= Build.VERSION_CODES.S) {
-                throw new AndroidTimeoutException("Timeout waiting for provider '" + name + "'");
-            }
             return null;
         }
 
@@ -7587,12 +7580,8 @@
 
     private ContentProviderHolder getContentProviderExternalUnchecked(String name,
             IBinder token, int callingUid, String callingTag, int userId) {
-        try {
-            return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
-                    true, userId);
-        } catch (AndroidTimeoutException ate) {
-            return null;
-        }
+        return getContentProviderImpl(null, name, token, callingUid, null, callingTag,
+                true, userId);
     }
 
     /**
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 260082f..2eca00e 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1606,7 +1606,8 @@
         packageUpdateFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
         packageUpdateFilter.addDataScheme("package");
 
-        mContext.registerReceiver(mOnPackageUpdatedReceiver, packageUpdateFilter);
+        mContext.registerReceiverAsUser(mOnPackageUpdatedReceiver, UserHandle.ALL,
+                packageUpdateFilter, null, null);
 
         synchronized (this) {
             for (int uidNum = mUidStates.size() - 1; uidNum >= 0; uidNum--) {
@@ -1649,7 +1650,7 @@
         final IntentFilter packageSuspendFilter = new IntentFilter();
         packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
         packageSuspendFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
-        mContext.registerReceiver(new BroadcastReceiver() {
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 final int[] changedUids = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
@@ -1673,7 +1674,7 @@
                     }
                 }
             }
-        }, packageSuspendFilter);
+        }, UserHandle.ALL, packageSuspendFilter, null, null);
 
         final IntentFilter packageAddedFilter = new IntentFilter();
         packageAddedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 36beb39..4fd9ae2 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -9002,10 +9002,15 @@
         if (DEBUG_VOL) {
             Log.d(TAG, "Persisting Volume Behavior for DeviceType: " + deviceType);
         }
-        System.putIntForUser(mContentResolver,
-                getSettingsNameForDeviceVolumeBehavior(deviceType),
-                deviceVolumeBehavior,
-                UserHandle.USER_CURRENT);
+        long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            System.putIntForUser(mContentResolver,
+                    getSettingsNameForDeviceVolumeBehavior(deviceType),
+                    deviceVolumeBehavior,
+                    UserHandle.USER_CURRENT);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
     }
 
     @AudioManager.DeviceVolumeBehaviorState
diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
index 7a4f70a..08a6171 100644
--- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java
+++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java
@@ -32,6 +32,9 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.biometrics.sensors.LockoutTracker;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -54,6 +57,8 @@
     static final int BIOMETRIC_NOT_ENROLLED = 7;
     static final int BIOMETRIC_NOT_ENABLED_FOR_APPS = 8;
     static final int CREDENTIAL_NOT_ENROLLED = 9;
+    static final int BIOMETRIC_LOCKOUT_TIMED = 10;
+    static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
     @IntDef({AUTHENTICATOR_OK,
             BIOMETRIC_NO_HARDWARE,
             BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
@@ -62,7 +67,9 @@
             BIOMETRIC_HARDWARE_NOT_DETECTED,
             BIOMETRIC_NOT_ENROLLED,
             BIOMETRIC_NOT_ENABLED_FOR_APPS,
-            CREDENTIAL_NOT_ENROLLED})
+            CREDENTIAL_NOT_ENROLLED,
+            BIOMETRIC_LOCKOUT_TIMED,
+            BIOMETRIC_LOCKOUT_PERMANENT})
     @Retention(RetentionPolicy.SOURCE)
     @interface AuthenticatorStatus {}
 
@@ -73,7 +80,7 @@
     // Sensors that can be used for this request (e.g. strong enough, enrolled, enabled).
     final List<BiometricSensor> eligibleSensors;
     // Sensors that cannot be used for this request. Pair<BiometricSensor, AuthenticatorStatus>
-    private final List<Pair<BiometricSensor, Integer>> mIneligibleSensors;
+    final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
     final boolean credentialAvailable;
     final boolean confirmationRequested;
 
@@ -156,6 +163,14 @@
             if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)) {
                 return BIOMETRIC_NOT_ENROLLED;
             }
+
+            final @LockoutTracker.LockoutMode int lockoutMode =
+                    sensor.impl.getLockoutModeForUser(userId);
+            if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
+                return BIOMETRIC_LOCKOUT_TIMED;
+            } else if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
+                return BIOMETRIC_LOCKOUT_PERMANENT;
+            }
         } catch (RemoteException e) {
             return BIOMETRIC_HARDWARE_NOT_DETECTED;
         }
@@ -232,7 +247,7 @@
         this.credentialRequested = credentialRequested;
 
         this.eligibleSensors = eligibleSensors;
-        this.mIneligibleSensors = ineligibleSensors;
+        this.ineligibleSensors = ineligibleSensors;
         this.credentialAvailable = credentialAvailable;
         this.confirmationRequested = confirmationRequested;
     }
@@ -258,9 +273,9 @@
                 }
             } else {
                 // Pick the first sensor error if it exists
-                if (!mIneligibleSensors.isEmpty()) {
-                    modality |= mIneligibleSensors.get(0).first.modality;
-                    status = mIneligibleSensors.get(0).second;
+                if (!ineligibleSensors.isEmpty()) {
+                    modality |= ineligibleSensors.get(0).first.modality;
+                    status = ineligibleSensors.get(0).second;
                 } else {
                     modality |= TYPE_CREDENTIAL;
                     status = CREDENTIAL_NOT_ENROLLED;
@@ -274,9 +289,9 @@
                  }
             } else {
                 // Pick the first sensor error if it exists
-                if (!mIneligibleSensors.isEmpty()) {
-                    modality |= mIneligibleSensors.get(0).first.modality;
-                    status = mIneligibleSensors.get(0).second;
+                if (!ineligibleSensors.isEmpty()) {
+                    modality |= ineligibleSensors.get(0).first.modality;
+                    status = ineligibleSensors.get(0).second;
                 } else {
                     modality |= TYPE_NONE;
                     status = BIOMETRIC_NO_HARDWARE;
@@ -293,7 +308,7 @@
         }
 
         Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
-                + " AuthenticatorSatus: " + status);
+                + " AuthenticatorStatus: " + status);
         return new Pair<>(modality, status);
     }
 
@@ -325,6 +340,8 @@
             case BIOMETRIC_HARDWARE_NOT_DETECTED:
             case BIOMETRIC_NOT_ENROLLED:
             case CREDENTIAL_NOT_ENROLLED:
+            case BIOMETRIC_LOCKOUT_TIMED:
+            case BIOMETRIC_LOCKOUT_PERMANENT:
                 break;
 
             case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
@@ -379,7 +396,7 @@
         string.append("}");
 
         string.append("\nIneligible:{");
-        for (Pair<BiometricSensor, Integer> ineligible : mIneligibleSensors) {
+        for (Pair<BiometricSensor, Integer> ineligible : ineligibleSensors) {
             string.append(ineligible.first).append(":").append(ineligible.second).append(" ");
         }
         string.append("}");
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 0d4d589..6be4574 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -23,6 +23,8 @@
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_HARDWARE_NOT_DETECTED;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_INSUFFICIENT_STRENGTH;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE;
+import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_LOCKOUT_PERMANENT;
+import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_LOCKOUT_TIMED;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
 import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
@@ -235,6 +237,10 @@
             case BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
                 break;
+            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
+            case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
+                biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
+                break;
             default:
                 Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
@@ -289,6 +295,12 @@
             case CREDENTIAL_NOT_ENROLLED:
                 return BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL;
 
+            case BIOMETRIC_LOCKOUT_TIMED:
+                return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT;
+
+            case BIOMETRIC_LOCKOUT_PERMANENT:
+                return BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+
             case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
             case BIOMETRIC_HARDWARE_NOT_DETECTED:
             case BIOMETRIC_NOT_ENABLED_FOR_APPS:
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 92fc33e..466465e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -32,7 +32,7 @@
  * Abstract {@link ClientMonitor} subclass that operations eligible/interested in acquisition
  * messages should extend.
  */
-public abstract class AcquisitionClient extends ClientMonitor
+public abstract class AcquisitionClient<T> extends ClientMonitor<T>
         implements ErrorConsumer, Cancellable {
 
     private static final String TAG = "Biometrics/AcquisitionClient";
@@ -47,18 +47,28 @@
     private final VibrationEffect mSuccessVibrationEffect;
     private final VibrationEffect mErrorVibrationEffect;
 
-    AcquisitionClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
+    AcquisitionClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int cookie, int sensorId, int statsModality,
             int statsAction, int statsClient) {
-        super(finishCallback, context, token, listener, userId, restricted, owner, cookie, sensorId,
-                statsModality, statsAction, statsClient);
+        super(context, token, listener, userId, owner, cookie, sensorId, statsModality, statsAction,
+                statsClient);
         mPowerManager = context.getSystemService(PowerManager.class);
         mSuccessVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
         mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
     }
 
     @Override
+    public void unableToStart() {
+        try {
+            getListener().onError(getSensorId(), getCookie(),
+                    BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send error", e);
+        }
+    }
+
+    @Override
     public void onError(int errorCode, int vendorCode) {
         logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
         try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
index e4e7715..a9cefba 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -22,6 +22,7 @@
 import android.app.TaskStackListener;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -33,7 +34,7 @@
 /**
  * A class to keep track of the authentication state for a given client.
  */
-public abstract class AuthenticationClient extends AcquisitionClient {
+public abstract class AuthenticationClient<T> extends AcquisitionClient<T> {
 
     private static final String TAG = "Biometrics/AuthenticationClient";
 
@@ -42,26 +43,29 @@
     private final IActivityTaskManager mActivityTaskManager;
     private final TaskStackListener mTaskStackListener;
     private final LockoutTracker mLockoutTracker;
+    private final boolean mIsRestricted;
 
     protected final long mOperationId;
 
     private long mStartTimeMs;
     private boolean mAlreadyCancelled;
 
-    public AuthenticationClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int targetUserId, long operationId, boolean restricted, @NonNull String owner,
-            int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
-            int statsModality, int statsClient, @NonNull TaskStackListener taskStackListener,
-            @NonNull LockoutTracker lockoutTracker) {
-        super(finishCallback, context, token, listener, targetUserId, restricted, owner, cookie,
-                sensorId, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
+    protected boolean mAuthAttempted;
+
+    public AuthenticationClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+            boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
+            int sensorId, boolean isStrongBiometric, int statsModality, int statsClient,
+            @NonNull TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker) {
+        super(context, token, listener, targetUserId, owner, cookie, sensorId,
+                statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
         mIsStrongBiometric = isStrongBiometric;
         mOperationId = operationId;
         mRequireConfirmation = requireConfirmation;
         mActivityTaskManager = ActivityTaskManager.getService();
         mTaskStackListener = taskStackListener;
         mLockoutTracker = lockoutTracker;
+        mIsRestricted = restricted;
     }
 
     public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
@@ -144,7 +148,7 @@
 
                     // Explicitly have if/else here to make it super obvious in case the code is
                     // touched in the future.
-                    if (!getIsRestricted()) {
+                    if (!mIsRestricted) {
                         listener.onAuthenticationSucceeded(getSensorId(), identifier, byteToken,
                                 getTargetUserId(), mIsStrongBiometric);
                     } else {
@@ -183,7 +187,20 @@
      * Start authentication
      */
     @Override
-    public void start() {
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
+        final @LockoutTracker.LockoutMode int lockoutMode =
+                mLockoutTracker.getLockoutModeForUser(getTargetUserId());
+        if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
+            Slog.v(TAG, "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
+            int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
+                    ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
+                    : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
+            onError(errorCode, 0 /* vendorCode */);
+            return;
+        }
+
         try {
             mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
         } catch (RemoteException e) {
@@ -193,6 +210,7 @@
         if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString());
 
         mStartTimeMs = System.currentTimeMillis();
+        mAuthAttempted = true;
         startHalOperation();
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
index 4c24d1a..a784b4b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
@@ -71,7 +71,7 @@
  *
  * @hide
  */
-public abstract class BiometricServiceBase extends SystemService
+public abstract class BiometricServiceBase<T> extends SystemService
         implements IHwBinder.DeathRecipient {
 
     protected static final boolean DEBUG = true;
@@ -83,7 +83,6 @@
     private final String mKeyguardPackage;
     protected final IActivityTaskManager mActivityTaskManager;
     private final PowerManager mPowerManager;
-    private final UserManager mUserManager;
     protected final BiometricTaskStackListener mTaskStackListener =
             new BiometricTaskStackListener();
     private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
@@ -120,7 +119,7 @@
             if (!hasEnrolledBiometrics(userId)) {
                 Slog.d(getTag(), "Last biometric removed for user: " + userId
                         + ", updating active group");
-                updateActiveGroup(userId, null);
+                updateActiveGroup(userId);
             }
         }
 
@@ -128,8 +127,8 @@
     };
 
     private IBiometricService mBiometricService;
-    private ClientMonitor mCurrentClient;
-    private ClientMonitor mPendingClient;
+    private ClientMonitor<T> mCurrentClient;
+    private ClientMonitor<T> mPendingClient;
     private PerformanceTracker mPerformanceTracker;
     private int mSensorId;
     protected int mCurrentUserId = UserHandle.USER_NULL;
@@ -140,6 +139,11 @@
     protected abstract String getTag();
 
     /**
+     * @return a fresh reference to the biometric HAL
+     */
+    protected abstract T getDaemon();
+
+    /**
      * @return the biometric utilities for a specific implementation.
      */
     protected abstract BiometricUtils getBiometricUtils();
@@ -153,9 +157,8 @@
     /**
      * Notifies the HAL that the user has changed.
      * @param userId
-     * @param clientPackage
      */
-    protected abstract void updateActiveGroup(int userId, String clientPackage);
+    protected abstract void updateActiveGroup(int userId);
 
     /**
      * @param userId
@@ -253,7 +256,7 @@
             FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                     statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
 
-            ClientMonitor newClient = mPendingClient;
+            ClientMonitor<T> newClient = mPendingClient;
             mCurrentClient = null;
             mPendingClient = null;
             startClient(newClient, false);
@@ -339,7 +342,6 @@
         mAppOps = context.getSystemService(AppOpsManager.class);
         mActivityTaskManager = ActivityTaskManager.getService();
         mPowerManager = mContext.getSystemService(PowerManager.class);
-        mUserManager = UserManager.get(mContext);
         mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
     }
 
@@ -456,7 +458,7 @@
             // When enrollment finishes, update this group's authenticator id, as the HAL has
             // already generated a new authenticator id when the new biometric is enrolled.
             if (identifier instanceof Fingerprint) {
-                updateActiveGroup(((Fingerprint)identifier).getGroupId(), null);
+                updateActiveGroup(((Fingerprint)identifier).getGroupId());
             }
         }
     }
@@ -520,7 +522,7 @@
      * Calls from the Manager. These are still on the calling binder's thread.
      */
 
-    protected void enrollInternal(EnrollClient client, int userId) {
+    protected void enrollInternal(EnrollClient<T> client, int userId) {
         if (hasReachedEnrollmentLimit(userId)) {
             return;
         }
@@ -546,26 +548,26 @@
         });
     }
 
-    protected void generateChallengeInternal(GenerateChallengeClient client) {
+    protected void generateChallengeInternal(GenerateChallengeClient<T> client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
     }
 
-    protected void revokeChallengeInternal(RevokeChallengeClient client) {
+    protected void revokeChallengeInternal(RevokeChallengeClient<T> client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
     }
 
-    protected void authenticateInternal(AuthenticationClient client, String opPackageName) {
+    protected void authenticateInternal(AuthenticationClient<T> client, String opPackageName) {
         final int callingUid = Binder.getCallingUid();
         final int callingPid = Binder.getCallingPid();
         final int callingUserId = UserHandle.getCallingUserId();
         authenticateInternal(client, opPackageName, callingUid, callingPid, callingUserId);
     }
 
-    protected void authenticateInternal(AuthenticationClient client,
+    protected void authenticateInternal(AuthenticationClient<T> client,
             String opPackageName, int callingUid, int callingPid, int callingUserId) {
         if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
                 callingUserId)) {
@@ -622,13 +624,13 @@
         });
     }
 
-    protected void removeInternal(RemovalClient client) {
+    protected void removeInternal(RemovalClient<T> client) {
         mHandler.post(() -> {
             startClient(client, true /* initiatedByClient */);
         });
     }
 
-    protected void cleanupInternal(InternalCleanupClient client) {
+    protected void cleanupInternal(InternalCleanupClient<T> client) {
         mHandler.post(() -> {
             if (DEBUG) {
                 Slog.v(getTag(), "Cleaning up templates for user("
@@ -639,18 +641,9 @@
     }
 
     // Should be done on a handler thread - not on the Binder's thread.
-    private void startAuthentication(AuthenticationClient client, String opPackageName) {
+    private void startAuthentication(AuthenticationClient<T> client, String opPackageName) {
         if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
 
-        @LockoutTracker.LockoutMode int lockoutMode = getLockoutMode(client.getTargetUserId());
-        if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
-            Slog.v(getTag(), "In lockout mode(" + lockoutMode + ") ; disallowing authentication");
-            int errorCode = lockoutMode == LockoutTracker.LOCKOUT_TIMED
-                    ? BiometricConstants.BIOMETRIC_ERROR_LOCKOUT
-                    : BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT;
-            client.onError(errorCode, 0 /* vendorCode */);
-            return;
-        }
         startClient(client, true /* initiatedByClient */);
     }
 
@@ -751,7 +744,7 @@
      * @param initiatedByClient true for authenticate, remove and enroll
      */
     @VisibleForTesting
-    void startClient(ClientMonitor newClient, boolean initiatedByClient) {
+    protected void startClient(ClientMonitor<T> newClient, boolean initiatedByClient) {
         ClientMonitor currentClient = mCurrentClient;
         if (currentClient != null) {
             if (DEBUG) Slog.v(getTag(), "request stop current client " +
@@ -811,7 +804,7 @@
             return;
         }
 
-        if (DEBUG) Slog.v(getTag(), "starting client "
+        if (DEBUG) Slog.v(getTag(), "Starting client "
                 + mCurrentClient.getClass().getSimpleName()
                 + "(" + mCurrentClient.getOwnerString() + ")"
                 + " targetUserId: " + mCurrentClient.getTargetUserId()
@@ -823,7 +816,16 @@
             return;
         }
 
-        mCurrentClient.start();
+        final T daemon = getDaemon();
+        if (daemon == null) {
+            Slog.e(getTag(), "Daemon null, unable to start: "
+                    + mCurrentClient.getClass().getSimpleName());
+            mCurrentClient.unableToStart();
+            mCurrentClient = null;
+            return;
+        }
+
+        mCurrentClient.start(daemon, mClientFinishCallback);
         notifyClientActiveCallbacks(true);
     }
 
@@ -855,9 +857,9 @@
         long t = System.currentTimeMillis();
         mAuthenticatorIds.clear();
         for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
-            int userId = getUserOrWorkProfileId(null, user.id);
+            int userId = user.id;
             if (!mAuthenticatorIds.containsKey(userId)) {
-                updateActiveGroup(userId, null);
+                updateActiveGroup(userId);
             }
         }
 
@@ -867,17 +869,6 @@
         }
     }
 
-    /**
-     * @param clientPackage the package of the caller
-     * @return the profile id
-     */
-    protected int getUserOrWorkProfileId(String clientPackage, int userId) {
-        if (!isKeyguard(clientPackage) && isWorkProfile(userId)) {
-            return userId;
-        }
-        return getEffectiveUserId(userId);
-    }
-
     protected boolean isRestricted() {
         // Only give privileged apps (like Settings) access to biometric info
         final boolean restricted = !hasPermission(getManageBiometricPermission());
@@ -920,8 +911,7 @@
      * @return authenticator id for the calling user
      */
     protected long getAuthenticatorId(int callingUserId) {
-        final int userId = getUserOrWorkProfileId(null /* clientPackage */, callingUserId);
-        return mAuthenticatorIds.getOrDefault(userId, 0L);
+        return mAuthenticatorIds.getOrDefault(callingUserId, 0L);
     }
 
     /**
@@ -937,7 +927,7 @@
         if (getCurrentClient() instanceof InternalCleanupClient) {
             Slog.w(getTag(), "User switched while performing cleanup");
         }
-        updateActiveGroup(userId, null);
+        updateActiveGroup(userId);
         doTemplateCleanupForUser(userId);
     }
 
@@ -947,41 +937,12 @@
         }
     }
 
-    /**
-     * @param userId
-     * @return true if this is a work profile
-     */
-    private boolean isWorkProfile(int userId) {
-        UserInfo userInfo = null;
-        final long token = Binder.clearCallingIdentity();
-        try {
-            userInfo = mUserManager.getUserInfo(userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-        return userInfo != null && userInfo.isManagedProfile();
-    }
-
-
-    private int getEffectiveUserId(int userId) {
-        UserManager um = UserManager.get(mContext);
-        if (um != null) {
-            final long callingIdentity = Binder.clearCallingIdentity();
-            userId = um.getCredentialOwnerProfile(userId);
-            Binder.restoreCallingIdentity(callingIdentity);
-        } else {
-            Slog.e(getTag(), "Unable to acquire UserManager");
-        }
-        return userId;
-    }
-
-
     private void listenForUserSwitches() {
         try {
             ActivityManager.getService().registerUserSwitchObserver(
                     new SynchronousUserSwitchObserver() {
                         @Override
-                        public void onUserSwitching(int newUserId) throws RemoteException {
+                        public void onUserSwitching(int newUserId) {
                             mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
                                     .sendToTarget();
                         }
@@ -991,8 +952,7 @@
         }
     }
 
-    private void removeLockoutResetCallback(
-            LockoutResetMonitor monitor) {
+    private void removeLockoutResetCallback(LockoutResetMonitor monitor) {
         mLockoutMonitors.remove(monitor);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index ea1fde9..5a2d63d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -31,7 +31,7 @@
  * the current client.  Subclasses are responsible for coordinating the interaction with
  * the biometric's HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
  */
-public abstract class ClientMonitor extends LoggableMonitor implements IBinder.DeathRecipient {
+public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinder.DeathRecipient {
 
     private static final String TAG = "Biometrics/ClientMonitor";
     protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
@@ -51,31 +51,26 @@
         void onClientFinished(ClientMonitor clientMonitor);
     }
 
-    protected final FinishCallback mFinishCallback;
-
-    private final Context mContext;
+    @NonNull private final Context mContext;
     private final int mTargetUserId;
-    // True if client does not have MANAGE_FINGERPRINT permission
-    private final boolean mIsRestricted;
-    private final String mOwner;
+    @NonNull private final String mOwner;
     private final int mSensorId; // sensorId as configured by the framework
 
-    private IBinder mToken;
-    private ClientMonitorCallbackConverter mListener;
+    @Nullable private IBinder mToken;
+    @Nullable private ClientMonitorCallbackConverter mListener;
     // Currently only used for authentication client. The cookie generated by BiometricService
     // is never 0.
     private final int mCookie;
     boolean mAlreadyDone;
 
+    @NonNull protected T mDaemon;
+    @NonNull protected FinishCallback mFinishCallback;
+
     /**
-     * @param finishCallback    used to notify the ClientMonitor's holder when its lifecycle has
-     *                          completed and can be removed from the scheduler
      * @param context    system_server context
      * @param token      a unique token for the client
      * @param listener   recipient of related events (e.g. authentication)
      * @param userId     target user id for operation
-     * @param restricted whether or not client has the MANAGE_* permission
-     *                   permission
      * @param owner      name of the client that owns this
      * @param cookie     BiometricPrompt authentication cookie (to be moved into a subclass soon)
      * @param sensorId   ID of the sensor that the operation should be requested of
@@ -83,17 +78,15 @@
      * @param statsAction   One of {@link BiometricsProtoEnums} ACTION_* constants
      * @param statsClient   One of {@link BiometricsProtoEnums} CLIENT_* constants
      */
-    public ClientMonitor(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId,
-            boolean restricted, @NonNull String owner, int cookie, int sensorId, int statsModality,
-            int statsAction, int statsClient) {
+    public ClientMonitor(@NonNull Context context, @Nullable IBinder token,
+            @Nullable ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+            int cookie, int sensorId, int statsModality, int statsAction,
+            int statsClient) {
         super(statsModality, statsAction, statsClient);
-        mFinishCallback = finishCallback;
         mContext = context;
         mToken = token;
         mListener = listener;
         mTargetUserId = userId;
-        mIsRestricted = restricted;
         mOwner = owner;
         mCookie = cookie;
         mSensorId = sensorId;
@@ -112,10 +105,22 @@
     }
 
     /**
+     * Invoked if the scheduler is unable to start the ClientMonitor (for example the HAL is null).
+     * If such a problem is detected, the scheduler will not invoke
+     * {@link #start(Object, FinishCallback)}.
+     */
+    public abstract void unableToStart();
+
+    /**
      * Starts the ClientMonitor's lifecycle. Invokes {@link #startHalOperation()} when internal book
      * keeping is complete.
+     * @param daemon reference to the HAL
+     * @param finishCallback invoked when the operation is complete (succeeds, fails, etc)
      */
-    public abstract void start();
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        mDaemon = daemon;
+        mFinishCallback = finishCallback;
+    }
 
     /**
      * Starts the HAL operation specific to the ClientMonitor subclass.
@@ -174,10 +179,6 @@
         return mListener;
     }
 
-    public final boolean getIsRestricted() {
-        return mIsRestricted;
-    }
-
     public final int getTargetUserId() {
         return mTargetUserId;
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index f4863f59..ad14531 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
@@ -126,4 +126,17 @@
             mFingerprintServiceReceiver.onChallengeGenerated(challenge);
         }
     }
+
+    public void onFeatureSet(boolean success, int feature) throws RemoteException {
+        if (mFaceServiceReceiver != null) {
+            mFaceServiceReceiver.onFeatureSet(success, feature);
+        }
+    }
+
+    public void onFeatureGet(boolean success, int feature, boolean value)
+            throws RemoteException {
+        if (mFaceServiceReceiver != null) {
+            mFaceServiceReceiver.onFeatureGet(success, feature, value);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
index 4c9f68f..6637ede 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -29,7 +29,7 @@
 /**
  * A class to keep track of the enrollment state for a given client.
  */
-public abstract class EnrollClient extends AcquisitionClient {
+public abstract class EnrollClient<T> extends AcquisitionClient<T> {
 
     private static final String TAG = "Biometrics/EnrollClient";
 
@@ -41,14 +41,13 @@
     private long mEnrollmentStartTimeMs;
     private boolean mAlreadyCancelled;
 
-    public EnrollClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, boolean restricted, String owner,
-            @NonNull BiometricUtils utils, int timeoutSec, int statsModality, int sensorId,
+    public EnrollClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+            int timeoutSec, int statsModality, int sensorId,
             boolean shouldVibrate) {
-        super(finishCallback, context, token, listener, userId, restricted, owner, 0 /* cookie */,
-                sensorId, statsModality, BiometricsProtoEnums.ACTION_ENROLL,
-                BiometricsProtoEnums.CLIENT_UNKNOWN);
+        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality,
+                BiometricsProtoEnums.ACTION_ENROLL, BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricUtils = utils;
         mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length);
         mTimeoutSec = timeoutSec;
@@ -88,7 +87,9 @@
     }
 
     @Override
-    public void start() {
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
         mEnrollmentStartTimeMs = System.currentTimeMillis();
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
index 98e83da..12584cf 100644
--- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java
@@ -16,27 +16,39 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
 
-public abstract class GenerateChallengeClient extends ClientMonitor {
+public abstract class GenerateChallengeClient<T> extends ClientMonitor<T> {
 
     private static final String TAG = "GenerateChallengeClient";
 
     protected long mChallenge;
 
-    public GenerateChallengeClient(FinishCallback finishCallback, Context context, IBinder token,
+    public GenerateChallengeClient(Context context, IBinder token,
             ClientMonitorCallbackConverter listener, String owner, int sensorId) {
-        super(finishCallback, context, token, listener, 0 /* userId */, false /* restricted */,
-                owner, 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
-                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+        super(context, token, listener, 0 /* userId */, owner, 0 /* cookie */, sensorId,
+                BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+                BiometricsProtoEnums.CLIENT_UNKNOWN);
     }
 
     @Override
-    public void start() {
+    public void unableToStart() {
+        try {
+            getListener().onChallengeGenerated(0L);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send error", e);
+        }
+    }
+
+    @Override
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
         startHalOperation();
         try {
             getListener().onChallengeGenerated(mChallenge);
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
index e2e96e3..f3ade65 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java
@@ -37,8 +37,8 @@
  * 2) The HAL and Framework are not in sync, and
  * {@link #onRemoved(BiometricAuthenticator.Identifier, int)} returns true/
  */
-public abstract class InternalCleanupClient extends ClientMonitor implements EnumerateConsumer,
-        RemovalConsumer {
+public abstract class InternalCleanupClient<T> extends ClientMonitor<T>
+        implements EnumerateConsumer, RemovalConsumer {
 
     private static final String TAG = "Biometrics/InternalCleanupClient";
 
@@ -58,17 +58,17 @@
     private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>();
     private final BiometricUtils mBiometricUtils;
     private final List<? extends BiometricAuthenticator.Identifier> mEnrolledList;
-    private ClientMonitor mCurrentTask;
+    private ClientMonitor<T> mCurrentTask;
 
     private final FinishCallback mEnumerateFinishCallback = clientMonitor -> {
         final List<BiometricAuthenticator.Identifier> unknownHALTemplates =
-                ((InternalEnumerateClient) mCurrentTask).getUnknownHALTemplates();
+                ((InternalEnumerateClient<T>) mCurrentTask).getUnknownHALTemplates();
 
         if (!unknownHALTemplates.isEmpty()) {
             Slog.w(TAG, "Adding " + unknownHALTemplates.size() + " templates for deletion");
         }
-        for (int i = 0; i < unknownHALTemplates.size(); i++) {
-            mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplates.get(i),
+        for (BiometricAuthenticator.Identifier unknownHALTemplate : unknownHALTemplates) {
+            mUnknownHALTemplates.add(new UserTemplate(unknownHALTemplate,
                     mCurrentTask.getTargetUserId()));
         }
 
@@ -85,22 +85,20 @@
         mFinishCallback.onClientFinished(this);
     };
 
-    protected abstract InternalEnumerateClient getEnumerateClient(FinishCallback finishCallback,
-            Context context, IBinder token, int userId, boolean restricted, String owner,
+    protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, IBinder token,
+            int userId, String owner,
             List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
-            int sensorId, int statsModality);
+            int sensorId);
 
-    protected abstract RemovalClient getRemovalClient(FinishCallback finishCallback,
-            Context context, IBinder token, int biometricId, int userId, boolean restricted,
-            String owner, BiometricUtils utils, int sensorId, int statsModality);
+    protected abstract RemovalClient<T> getRemovalClient(Context context, IBinder token,
+            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId);
 
-    protected InternalCleanupClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, int userId, boolean restricted,
+    protected InternalCleanupClient(@NonNull Context context, int userId,
             @NonNull String owner, int sensorId, int statsModality,
             @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
             @NonNull BiometricUtils utils) {
-        super(finishCallback, context, null /* token */, null /* ClientMonitorCallbackConverter */,
-                userId, restricted, owner, 0 /* cookie */, sensorId, statsModality,
+        super(context, null /* token */, null /* ClientMonitorCallbackConverter */,
+                userId, owner, 0 /* cookie */, sensorId, statsModality,
                 BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricUtils = utils;
         mEnrolledList = enrolledList;
@@ -109,22 +107,28 @@
     private void startCleanupUnknownHalTemplates() {
         UserTemplate template = mUnknownHALTemplates.get(0);
         mUnknownHALTemplates.remove(template);
-        mCurrentTask = getRemovalClient(mRemoveFinishCallback, getContext(), getToken(),
-                template.mIdentifier.getBiometricId(), template.mUserId, getIsRestricted(),
-                getContext().getPackageName(), mBiometricUtils, getSensorId(), mStatsModality);
+        mCurrentTask = getRemovalClient(getContext(), getToken(),
+                template.mIdentifier.getBiometricId(), template.mUserId,
+                getContext().getPackageName(), mBiometricUtils, getSensorId());
         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
                 mStatsModality,
                 BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_HAL);
-        mCurrentTask.start();
+        mCurrentTask.start(mDaemon, mRemoveFinishCallback);
     }
 
     @Override
-    public void start() {
+    public void unableToStart() {
+        // nothing to do here
+    }
+
+    @Override
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
         // Start enumeration. Removal will start if necessary, when enumeration is completed.
-        mCurrentTask = getEnumerateClient(mEnumerateFinishCallback, getContext(), getToken(),
-                getTargetUserId(), getIsRestricted(), getOwnerString(), mEnrolledList,
-                mBiometricUtils, getSensorId(), mStatsModality);
-        mCurrentTask.start();
+        mCurrentTask = getEnumerateClient(getContext(), getToken(), getTargetUserId(),
+                getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId());
+        mCurrentTask.start(daemon, mEnumerateFinishCallback);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
index 6764b21..9ce271c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java
@@ -31,7 +31,8 @@
 /**
  * Internal class to help clean up unknown templates in the HAL and Framework
  */
-public abstract class InternalEnumerateClient extends ClientMonitor implements EnumerateConsumer {
+public abstract class InternalEnumerateClient<T> extends ClientMonitor<T>
+        implements EnumerateConsumer {
 
     private static final String TAG = "Biometrics/InternalEnumerateClient";
 
@@ -42,16 +43,14 @@
     // List of templates to remove from the HAL
     private List<BiometricAuthenticator.Identifier> mUnknownHALTemplates = new ArrayList<>();
 
-    protected InternalEnumerateClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBinder token, int userId, boolean restricted,
+    protected InternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
             @NonNull String owner,
             @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
             @NonNull BiometricUtils utils, int sensorId, int statsModality) {
         // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
-        super(finishCallback, context, token, null /* ClientMonitorCallbackConverter */, userId,
-                restricted, owner, 0 /* cookie */, sensorId, statsModality,
-                BiometricsProtoEnums.ACTION_ENUMERATE,
+        super(context, token, null /* ClientMonitorCallbackConverter */, userId, owner,
+                0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE,
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
         mEnrolledList = enrolledList;
         mUtils = utils;
@@ -68,7 +67,14 @@
     }
 
     @Override
-    public void start() {
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
         // The biometric template ids will be removed when we get confirmation from the HAL
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
index 661f7fc..b734516 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java
@@ -27,26 +27,31 @@
 /**
  * A class to keep track of the remove state for a given client.
  */
-public abstract class RemovalClient extends ClientMonitor implements RemovalConsumer {
+public abstract class RemovalClient<T> extends ClientMonitor<T> implements RemovalConsumer {
 
     private static final String TAG = "Biometrics/RemovalClient";
 
     protected final int mBiometricId;
     private final BiometricUtils mBiometricUtils;
 
-    public RemovalClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int biometricId, int userId, boolean restricted, @NonNull String owner,
-            @NonNull BiometricUtils utils, int sensorId, int statsModality) {
-        super(finishCallback, context, token, listener, userId, restricted, owner, 0 /* cookie */,
-                sensorId, statsModality, BiometricsProtoEnums.ACTION_REMOVE,
-                BiometricsProtoEnums.CLIENT_UNKNOWN);
+    public RemovalClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
+            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId, int statsModality) {
+        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality,
+                BiometricsProtoEnums.ACTION_REMOVE, BiometricsProtoEnums.CLIENT_UNKNOWN);
         mBiometricId = biometricId;
         mBiometricUtils = utils;
     }
 
     @Override
-    public void start() {
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
         // The biometric template ids will be removed when we get confirmation from the HAL
         startHalOperation();
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
index 024ff57..e00396b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java
@@ -16,22 +16,28 @@
 
 package com.android.server.biometrics.sensors;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricsProtoEnums;
 import android.os.IBinder;
 
-public abstract class RevokeChallengeClient extends ClientMonitor {
+public abstract class RevokeChallengeClient<T> extends ClientMonitor<T> {
 
-    public RevokeChallengeClient(FinishCallback finishCallback, Context context, IBinder token,
-            String owner, int sensorId) {
-        super(finishCallback, context, token, null /* listener */, 0 /* userId */,
-                false /* restricted */, owner, 0 /* cookie */, sensorId,
+    public RevokeChallengeClient(Context context, IBinder token, String owner, int sensorId) {
+        super(context, token, null /* listener */, 0 /* userId */, owner, 0 /* cookie */, sensorId,
                 BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
                 BiometricsProtoEnums.CLIENT_UNKNOWN);
     }
 
     @Override
-    public void start() {
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    public void start(@NonNull T daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
         startHalOperation();
         mFinishCallback.onClientFinished(this);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
index 82bef34..a54357e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
@@ -48,11 +48,10 @@
  * Face-specific authentication client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
  */
-class FaceAuthenticationClient extends AuthenticationClient {
+class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
 
     private static final String TAG = "FaceAuthenticationClient";
 
-    private final IBiometricsFace mDaemon;
     private final NotificationManager mNotificationManager;
     private final UsageStats mUsageStats;
 
@@ -62,23 +61,17 @@
     private final int[] mKeyguardIgnoreListVendor;
 
     private int mLastAcquire;
-    // We need to track this state since it's possible for applications to request for
-    // authentication while the device is already locked out. In that case, the client is created
-    // but not started yet. The user shouldn't receive the error haptics in this case.
-    private boolean mStarted;
 
-    FaceAuthenticationClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFace daemon, @NonNull IBinder token,
+    FaceAuthenticationClient(@NonNull Context context, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
             boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
             boolean isStrongBiometric, int statsClient,
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats) {
-        super(finishCallback, context, token, listener, targetUserId, operationId, restricted,
+        super(context, token, listener, targetUserId, operationId, restricted,
                 owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
                 BiometricsProtoEnums.MODALITY_FACE, statsClient, taskStackListener,
                 lockoutTracker);
-        mDaemon = daemon;
         mNotificationManager = context.getSystemService(NotificationManager.class);
         mUsageStats = usageStats;
 
@@ -94,18 +87,6 @@
     }
 
     @Override
-    public void start() {
-        mStarted = true;
-        super.start();
-    }
-
-    @Override
-    public void cancel() {
-        mStarted = false;
-        super.cancel();
-    }
-
-    @Override
     protected void startHalOperation() {
         try {
             mDaemon.authenticate(mOperationId);
@@ -139,10 +120,6 @@
             boolean authenticated, ArrayList<Byte> token) {
         super.onAuthenticated(identifier, authenticated, token);
 
-        if (authenticated) {
-            mStarted = false;
-        }
-
         mUsageStats.addEvent(new UsageStats.AuthenticationEvent(
                 getStartTimeMs(),
                 System.currentTimeMillis() - getStartTimeMs() /* latency */,
@@ -176,7 +153,9 @@
                 }
             case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT:
             case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT:
-                if (mStarted) {
+                if (mAuthAttempted) {
+                    // Only vibrate if auth was attempted. If the user was already locked out prior
+                    // to starting authentication, do not vibrate.
                     vibrateError();
                 }
                 break;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
index a39c4b9..33244b8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticator.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 
 import com.android.server.biometrics.SensorConfig;
+import com.android.server.biometrics.sensors.LockoutTracker;
 
 /**
  * Shim that converts IFaceService into a common reusable IBiometricAuthenticator interface.
@@ -68,6 +69,12 @@
     }
 
     @Override
+    public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
+            throws RemoteException {
+        return mFaceService.getLockoutModeForUser(userId);
+    }
+
+    @Override
     public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
         mFaceService.resetLockout(userId, hardwareAuthToken);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index ee8aa17..b63b39e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -16,8 +16,11 @@
 
 package com.android.server.biometrics.sensors.face;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.hardware.biometrics.face.V1_0.Status;
 import android.hardware.face.FaceManager;
@@ -39,24 +42,22 @@
  * Face-specific enroll client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
  */
-public class FaceEnrollClient extends EnrollClient {
+public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
 
     private static final String TAG = "FaceEnrollClient";
 
-    private final IBiometricsFace mDaemon;
-    private final int[] mDisabledFeatures;
-    private final NativeHandle mSurfaceHandle;
-    private final int[] mEnrollIgnoreList;
-    private final int[] mEnrollIgnoreListVendor;
+    @NonNull private final int[] mDisabledFeatures;
+    @Nullable private final NativeHandle mSurfaceHandle;
+    @NonNull private final int[] mEnrollIgnoreList;
+    @NonNull private final int[] mEnrollIgnoreListVendor;
 
-    FaceEnrollClient(FinishCallback finishCallback, Context context, IBiometricsFace daemon,
-            IBinder token, ClientMonitorCallbackConverter listener, int userId,
-            byte[] hardwareAuthToken, boolean restricted, String owner, BiometricUtils utils,
-            int[] disabledFeatures, int timeoutSec, int statsModality, NativeHandle surfaceHandle,
+    FaceEnrollClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+            @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle,
             int sensorId) {
-        super(finishCallback, context, token, listener, userId, hardwareAuthToken, restricted,
-                owner, utils, timeoutSec, statsModality, sensorId, false /* shouldVibrate */);
-        mDaemon = daemon;
+        super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec,
+                BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */);
         mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length);
         mSurfaceHandle = surfaceHandle;
         mEnrollIgnoreList = getContext().getResources()
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
index 7d10c59..72188a5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGenerateChallengeClient.java
@@ -31,18 +31,14 @@
  * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
  * HIDL interfaces.
  */
-public class FaceGenerateChallengeClient extends GenerateChallengeClient {
+public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiometricsFace> {
 
     private static final String TAG = "FaceGenerateChallengeClient";
     private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes
 
-    private final IBiometricsFace mDaemon;
-
-    FaceGenerateChallengeClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBiometricsFace daemon, @NonNull IBinder token,
+    FaceGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
-        super(finishCallback, context, token, listener, owner, sensorId);
-        mDaemon = daemon;
+        super(context, token, listener, owner, sensorId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
new file mode 100644
index 0000000..227d817
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceGetFeatureClient.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.OptionalBool;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+/**
+ * Face-specific getFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceGetFeatureClient extends ClientMonitor<IBiometricsFace> {
+
+    private static final String TAG = "FaceGetFeatureClient";
+
+    private final int mFeature;
+    private final int mFaceId;
+
+    FaceGetFeatureClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner,
+            int sensorId, int feature, int faceId) {
+        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId,
+                BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+                BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mFeature = feature;
+        mFaceId = faceId;
+
+    }
+
+    @Override
+    public void unableToStart() {
+        try {
+            getListener().onFeatureGet(false /* success */, mFeature, false /* value */);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send error", e);
+        }
+    }
+
+    @Override
+    public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+        startHalOperation();
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            final OptionalBool result = mDaemon.getFeature(mFeature, mFaceId);
+            getListener().onFeatureGet(result.status == Status.OK, mFeature, result.value);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to getFeature", e);
+        }
+        mFinishCallback.onClientFinished(this);
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        // Not supported for GetFeature
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
index 2b4c4c8..388baa2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.os.IBinder;
 
@@ -34,36 +35,30 @@
  * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
  * HIDL interfaces.
  */
-class FaceInternalCleanupClient extends InternalCleanupClient {
-    private final IBiometricsFace mDaemon;
+class FaceInternalCleanupClient extends InternalCleanupClient<IBiometricsFace> {
 
-    FaceInternalCleanupClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFace daemon, int userId, boolean restricted, String owner,
-            int sensorId, int statsModality,
-            @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
+    FaceInternalCleanupClient(@NonNull Context context, int userId, @NonNull String owner,
+            int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
             @NonNull BiometricUtils utils) {
-        super(finishCallback, context, userId, restricted, owner, sensorId, statsModality,
-                enrolledList, utils);
-        mDaemon = daemon;
+        super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, enrolledList,
+                utils);
     }
 
     @Override
-    protected InternalEnumerateClient getEnumerateClient(FinishCallback finishCallback,
-            Context context, IBinder token, int userId, boolean restricted, String owner,
+    protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context,
+            IBinder token, int userId, String owner,
             List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            BiometricUtils utils, int sensorId, int statsModality) {
-        return new FaceInternalEnumerateClient(finishCallback, context, mDaemon, token, userId,
-                restricted, owner, enrolledList, utils, sensorId, statsModality);
+            BiometricUtils utils, int sensorId) {
+        return new FaceInternalEnumerateClient(context, token, userId, owner, enrolledList, utils,
+                sensorId);
     }
 
     @Override
-    protected RemovalClient getRemovalClient(FinishCallback finishCallback, Context context,
-            IBinder token, int biometricId, int userId, boolean restricted, String owner,
-            BiometricUtils utils, int sensorId, int statsModality) {
+    protected RemovalClient<IBiometricsFace> getRemovalClient(Context context, IBinder token,
+            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
-        return new FaceRemovalClient(finishCallback, context, mDaemon, token,
-                null /* ClientMonitorCallbackConverter */, biometricId, userId, restricted,
-                owner, utils, sensorId, statsModality);
+        return new FaceRemovalClient(context, token, null /* ClientMonitorCallbackConverter */,
+                biometricId, userId, owner, utils, sensorId);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
index 1b4597c..c6749c5 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -34,19 +35,15 @@
  * {@link android.hardware.biometrics.face.V1_0} and {@link android.hardware.biometrics.face.V1_1}
  * HIDL interfaces.
  */
-class FaceInternalEnumerateClient extends InternalEnumerateClient {
+class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFace> {
     private static final String TAG = "FaceInternalEnumerateClient";
 
-    private final IBiometricsFace mDaemon;
-
-    FaceInternalEnumerateClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFace daemon, @NonNull IBinder token, int userId, boolean restricted,
+    FaceInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
             @NonNull String owner,
             @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            @NonNull BiometricUtils utils, int sensorId, int statsModality) {
-        super(finishCallback, context, token, userId, restricted, owner, enrolledList, utils,
-                sensorId, statsModality);
-        mDaemon = daemon;
+            @NonNull BiometricUtils utils, int sensorId) {
+        super(context, token, userId, owner, enrolledList, utils, sensorId,
+                BiometricsProtoEnums.MODALITY_FACE);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
index 2c19397..b0ee981 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -31,18 +32,14 @@
  * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
  */
-class FaceRemovalClient extends RemovalClient {
+class FaceRemovalClient extends RemovalClient<IBiometricsFace> {
     private static final String TAG = "FaceRemovalClient";
-    private final IBiometricsFace mDaemon;
 
-    FaceRemovalClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFace daemon, @NonNull IBinder token,
+    FaceRemovalClient(@NonNull Context context, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            boolean restricted, @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
-            int statsModality) {
-        super(finishCallback, context, token, listener, biometricId, userId, restricted, owner,
-                utils, sensorId, statsModality);
-        mDaemon = daemon;
+            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) {
+        super(context, token, listener, biometricId, userId, owner, utils, sensorId,
+                BiometricsProtoEnums.MODALITY_FACE);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
new file mode 100644
index 0000000..441cb14
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceResetLockoutClient.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+
+import java.util.ArrayList;
+
+/**
+ * Face-specific resetLockout client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceResetLockoutClient extends ClientMonitor<IBiometricsFace> {
+
+    private static final String TAG = "FaceResetLockoutClient";
+
+    private final ArrayList<Byte> mHardwareAuthToken;
+
+    FaceResetLockoutClient(@NonNull Context context, int userId, String owner, int sensorId,
+            byte[] hardwareAuthToken) {
+        super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */,
+                sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN,
+                BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN);
+
+        mHardwareAuthToken = new ArrayList<>();
+        for (byte b : hardwareAuthToken) {
+            mHardwareAuthToken.add(b);
+        }
+    }
+
+    @Override
+    public void unableToStart() {
+        // Nothing to do here
+    }
+
+    @Override
+    public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
+        startHalOperation();
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            mDaemon.resetLockout(mHardwareAuthToken);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to reset lockout", e);
+        }
+        mFinishCallback.onClientFinished(this);
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        // Not supported for resetLockout
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
index 102efda..992a678d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRevokeChallengeClient.java
@@ -29,17 +29,13 @@
  * Face-specific revokeChallenge client supporting the {@link android.hardware.biometrics.face.V1_0}
  * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
  */
-public class FaceRevokeChallengeClient extends RevokeChallengeClient {
+public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometricsFace> {
 
     private static final String TAG = "FaceRevokeChallengeClient";
 
-    private final IBiometricsFace mDaemon;
-
-    FaceRevokeChallengeClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFace daemon, @NonNull IBinder token, @NonNull String owner,
-            int sensorId) {
-        super(finishCallback, context, token, owner, sensorId);
-        mDaemon = daemon;
+    FaceRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull String owner, int sensorId) {
+        super(context, token, owner, sensorId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 088762c..050fba2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -32,8 +32,6 @@
 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
 import android.hardware.biometrics.face.V1_0.IBiometricsFace;
 import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
-import android.hardware.biometrics.face.V1_0.OptionalBool;
-import android.hardware.biometrics.face.V1_0.Status;
 import android.hardware.face.Face;
 import android.hardware.face.IFaceService;
 import android.hardware.face.IFaceServiceReceiver;
@@ -87,7 +85,7 @@
  *
  * @hide
  */
-public class FaceService extends BiometricServiceBase {
+public class FaceService extends BiometricServiceBase<IBiometricsFace> {
 
     protected static final String TAG = "FaceService";
     private static final boolean DEBUG = true;
@@ -111,19 +109,12 @@
 
         @Override // Binder call
         public void generateChallenge(IBinder token, IFaceServiceReceiver receiver,
-                String opPackageName) throws RemoteException {
+                String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
 
-            final IBiometricsFace daemon = getFaceDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to generateChallenge, daemon null");
-                receiver.onChallengeGenerated(0L);
-                return;
-            }
-
-            final GenerateChallengeClient client = new FaceGenerateChallengeClient(
-                    mClientFinishCallback, getContext(), daemon, token,
-                    new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId());
+            final GenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(),
+                    token, new ClientMonitorCallbackConverter(receiver), opPackageName,
+                    getSensorId());
             generateChallengeInternal(client);
         }
 
@@ -131,14 +122,8 @@
         public void revokeChallenge(IBinder token, String owner) {
             checkPermission(MANAGE_BIOMETRIC);
 
-            final IBiometricsFace daemon = getFaceDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to revokeChallenge, daemon null");
-                return;
-            }
-
-            final RevokeChallengeClient client = new FaceRevokeChallengeClient(
-                    mClientFinishCallback, getContext(), daemon, token, owner, getSensorId());
+            final RevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(), token,
+                    owner, getSensorId());
 
             // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
             if (getCurrentClient() == null) {
@@ -153,37 +138,27 @@
         }
 
         @Override // Binder call
-        public void enroll(int userId, final IBinder token, final byte[] cryptoToken,
+        public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
-                final int[] disabledFeatures, Surface surface) throws RemoteException {
+                final int[] disabledFeatures, Surface surface) {
             checkPermission(MANAGE_BIOMETRIC);
-            updateActiveGroup(userId, opPackageName);
+            updateActiveGroup(userId);
 
             mHandler.post(() -> {
                 mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
                         UserHandle.CURRENT);
             });
 
-            final IBiometricsFace daemon = getFaceDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to enroll, daemon null");
-                receiver.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
-
-            final boolean restricted = isRestricted();
-            final EnrollClient client = new FaceEnrollClient(mClientFinishCallback, getContext(),
-                    daemon, token, new ClientMonitorCallbackConverter(receiver),
-                    userId, cryptoToken, restricted, opPackageName, getBiometricUtils(),
-                    disabledFeatures, ENROLL_TIMEOUT_SEC, statsModality(),
+            final EnrollClient client = new FaceEnrollClient(getContext(), token,
+                    new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
+                    opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC,
                     convertSurfaceToNativeHandle(surface), getSensorId());
 
             enrollInternal(client, userId);
         }
 
         @Override // Binder call
-        public void enrollRemotely(int userId, final IBinder token, final byte[] cryptoToken,
+        public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
                 final IFaceServiceReceiver receiver, final String opPackageName,
                 final int[] disabledFeatures) {
             checkPermission(MANAGE_BIOMETRIC);
@@ -198,27 +173,18 @@
 
         @Override // Binder call
         public void authenticate(final IBinder token, final long opId, int userId,
-                final IFaceServiceReceiver receiver, final int flags,
-                final String opPackageName) throws RemoteException {
+                final IFaceServiceReceiver receiver, final String opPackageName) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            updateActiveGroup(userId, opPackageName);
-
-            final IBiometricsFace daemon = getFaceDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to authenticate, daemon null");
-                receiver.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
+            updateActiveGroup(userId);
 
             final boolean restricted = isRestricted();
             final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
                     : BiometricsProtoEnums.CLIENT_UNKNOWN;
-            final AuthenticationClient client = new FaceAuthenticationClient(mClientFinishCallback,
-                    getContext(), daemon, token, new ClientMonitorCallbackConverter(receiver),
-                    userId, opId, restricted, opPackageName, 0 /* cookie */,
-                    false /* requireConfirmation */, getSensorId(), isStrongBiometric(),
-                    statsClient, mTaskStackListener, mLockoutTracker, mUsageStats);
+            final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token,
+                    new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
+                    opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
+                    isStrongBiometric(), statsClient, mTaskStackListener, mLockoutTracker,
+                    mUsageStats);
             authenticateInternal(client, opPackageName);
         }
 
@@ -226,23 +192,14 @@
         public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
                 int userId, IBiometricSensorReceiver sensorReceiver,
                 String opPackageName, int cookie, int callingUid, int callingPid,
-                int callingUserId) throws RemoteException {
+                int callingUserId) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
-            updateActiveGroup(userId, opPackageName);
-
-            final IBiometricsFace daemon = getFaceDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to prepare for authentication, daemon null");
-                sensorReceiver.onError(getSensorId(), cookie,
-                        BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
-                return;
-            }
+            updateActiveGroup(userId);
 
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClient client = new FaceAuthenticationClient(mClientFinishCallback,
-                    getContext(), daemon, token, new ClientMonitorCallbackConverter(sensorReceiver),
-                    userId, opId, restricted, opPackageName, cookie, requireConfirmation,
-                    getSensorId(), isStrongBiometric(),
+            final AuthenticationClient client = new FaceAuthenticationClient(getContext(), token,
+                    new ClientMonitorCallbackConverter(sensorReceiver), userId, opId, restricted,
+                    opPackageName, cookie, requireConfirmation, getSensorId(), isStrongBiometric(),
                     BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
                     mLockoutTracker, mUsageStats);
             authenticateInternal(client, opPackageName, callingUid, callingPid,
@@ -272,34 +229,23 @@
 
         @Override // Binder call
         public void remove(final IBinder token, final int faceId, final int userId,
-                final IFaceServiceReceiver receiver, final String opPackageName)
-                throws RemoteException {
+                final IFaceServiceReceiver receiver, final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
-            updateActiveGroup(userId, opPackageName);
+            updateActiveGroup(userId);
 
             if (token == null) {
                 Slog.w(TAG, "remove(): token is null");
                 return;
             }
 
-            final IBiometricsFace daemon = getFaceDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to remove, daemon null");
-                receiver.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
-
-            final boolean restricted = isRestricted();
-            final RemovalClient client = new FaceRemovalClient(mClientFinishCallback, getContext(),
-                    daemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId,
-                    restricted, opPackageName, getBiometricUtils(), getSensorId(), statsModality());
+            final RemovalClient client = new FaceRemovalClient(getContext(), token,
+                    new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
+                    getBiometricUtils(), getSensorId());
             removeInternal(client);
         }
 
         @Override
-        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
-                throws RemoteException {
+        public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             FaceService.super.addLockoutResetCallback(callback);
         }
@@ -369,6 +315,12 @@
             return FaceService.this.hasEnrolledBiometrics(userId);
         }
 
+        @Override
+        public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+            checkPermission(USE_BIOMETRIC_INTERNAL);
+            return mLockoutTracker.getLockoutModeForUser(userId);
+        }
+
         @Override // Binder call
         public long getAuthenticatorId(int callingUserId) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
@@ -386,65 +338,49 @@
 
                 Slog.d(TAG, "Resetting lockout for user: " + userId);
 
-                updateActiveGroup(userId, null /* opPackageName */);
-                try {
-                    final ArrayList<Byte> token = new ArrayList<>();
-                    for (int i = 0; i < hardwareAuthToken.length; i++) {
-                        token.add(hardwareAuthToken[i]);
-                    }
-                    mDaemon.resetLockout(token);
-                } catch (RemoteException e) {
-                    Slog.e(getTag(), "Unable to reset lockout", e);
-                }
+                updateActiveGroup(userId);
+                final FaceResetLockoutClient client = new FaceResetLockoutClient(getContext(),
+                        userId, getContext().getOpPackageName(), getSensorId(), hardwareAuthToken);
+                startClient(client, true /* initiatedByClient */);
             });
         }
 
         @Override
-        public void setFeature(int userId, int feature, boolean enabled, final byte[] token,
-                IFaceServiceReceiver receiver, final String opPackageName) {
+        public void setFeature(final IBinder token, int userId, int feature, boolean enabled,
+                final byte[] hardwareAuthToken, IFaceServiceReceiver receiver,
+                final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
 
             mHandler.post(() -> {
                 if (DEBUG) {
                     Slog.d(TAG, "setFeature for user(" + userId + ")");
                 }
-                updateActiveGroup(userId, opPackageName);
+                updateActiveGroup(userId);
                 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
                     Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
                     return;
                 }
 
-                final ArrayList<Byte> byteToken = new ArrayList<>();
-                for (int i = 0; i < token.length; i++) {
-                    byteToken.add(token[i]);
-                }
-
-                // TODO: Support multiple faces
                 final int faceId = getFirstTemplateForUser(mCurrentUserId);
 
-                if (mDaemon != null) {
-                    try {
-                        final int result = mDaemon.setFeature(feature, enabled, byteToken, faceId);
-                        receiver.onFeatureSet(result == Status.OK, feature);
-                    } catch (RemoteException e) {
-                        Slog.e(getTag(), "Unable to set feature: " + feature
-                                        + " to enabled:" + enabled, e);
-                    }
-                }
+                final FaceSetFeatureClient client = new FaceSetFeatureClient(getContext(),
+                        token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+                        getSensorId(), feature, enabled, hardwareAuthToken, faceId);
+                startClient(client, true /* initiatedByClient */);
             });
 
         }
 
         @Override
-        public void getFeature(int userId, int feature, IFaceServiceReceiver receiver,
-                final String opPackageName) {
+        public void getFeature(final IBinder token, int userId, int feature,
+                IFaceServiceReceiver receiver, final String opPackageName) {
             checkPermission(MANAGE_BIOMETRIC);
 
             mHandler.post(() -> {
                 if (DEBUG) {
                     Slog.d(TAG, "getFeature for user(" + userId + ")");
                 }
-                updateActiveGroup(userId, opPackageName);
+                updateActiveGroup(userId);
                 // This should ideally return tri-state, but the user isn't shown settings unless
                 // they are enrolled so it's fine for now.
                 if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
@@ -455,31 +391,14 @@
                 // TODO: Support multiple faces
                 final int faceId = getFirstTemplateForUser(mCurrentUserId);
 
-                if (mDaemon != null) {
-                    try {
-                        OptionalBool result = mDaemon.getFeature(feature, faceId);
-                        receiver.onFeatureGet(result.status == Status.OK, feature, result.value);
-                    } catch (RemoteException e) {
-                        Slog.e(getTag(), "Unable to getRequireAttention", e);
-                    }
-                }
+                final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(), token,
+                        new ClientMonitorCallbackConverter(receiver), userId, opPackageName,
+                        getSensorId(), feature, faceId);
+                startClient(client, true /* initiatedByClient */);
             });
 
         }
 
-        @Override
-        public void userActivity() {
-            checkPermission(MANAGE_BIOMETRIC);
-
-            if (mDaemon != null) {
-                try {
-                    mDaemon.userActivity();
-                } catch (RemoteException e) {
-                    Slog.e(getTag(), "Unable to send userActivity", e);
-                }
-            }
-        }
-
         // TODO: Support multiple faces
         private int getFirstTemplateForUser(int user) {
             final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
@@ -658,6 +577,11 @@
     }
 
     @Override
+    protected IBiometricsFace getDaemon() {
+        return getFaceDaemon();
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return FaceUtils.getInstance();
     }
@@ -683,12 +607,11 @@
     }
 
     @Override
-    protected void updateActiveGroup(int userId, String clientPackage) {
+    protected void updateActiveGroup(int userId) {
         IBiometricsFace daemon = getFaceDaemon();
 
         if (daemon != null) {
             try {
-                userId = getUserOrWorkProfileId(clientPackage, userId);
                 if (userId != mCurrentUserId) {
                     final File baseDir = Environment.getDataVendorDeDirectory(userId);
                     final File faceDir = new File(baseDir, FACE_DATA_DIR);
@@ -770,19 +693,10 @@
 
     @Override
     protected void doTemplateCleanupForUser(int userId) {
-        final IBiometricsFace daemon = getFaceDaemon();
-        if (daemon == null) {
-            Slog.e(TAG, "daemon null, skipping template cleanup");
-            return;
-        }
-
-        final boolean restricted = !hasPermission(getManageBiometricPermission());
         final List<? extends BiometricAuthenticator.Identifier> enrolledList =
                 getEnrolledTemplates(userId);
-        final FaceInternalCleanupClient client = new FaceInternalCleanupClient(
-                mClientFinishCallback, getContext(), daemon, userId, restricted,
-                getContext().getOpPackageName(), getSensorId(), statsModality(), enrolledList,
-                getBiometricUtils());
+        final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(), userId,
+                getContext().getOpPackageName(), getSensorId(), enrolledList, getBiometricUtils());
         cleanupInternal(client);
     }
 
@@ -816,7 +730,7 @@
             if (DEBUG) Slog.v(TAG, "Face HAL id: " + halId);
             if (halId != 0) {
                 loadAuthenticatorIds();
-                updateActiveGroup(ActivityManager.getCurrentUser(), null);
+                updateActiveGroup(ActivityManager.getCurrentUser());
                 doTemplateCleanupForUser(ActivityManager.getCurrentUser());
             } else {
                 Slog.w(TAG, "Failed to open Face HAL!");
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
new file mode 100644
index 0000000..91f63e1
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceSetFeatureClient.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics.sensors.face;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+
+import java.util.ArrayList;
+
+/**
+ * Face-specific setFeature client supporting the {@link android.hardware.biometrics.face.V1_0}
+ * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces.
+ */
+public class FaceSetFeatureClient extends ClientMonitor<IBiometricsFace> {
+
+    private static final String TAG = "FaceSetFeatureClient";
+
+    private final int mFeature;
+    private final boolean mEnabled;
+    private final ArrayList<Byte> mHardwareAuthToken;
+    private final int mFaceId;
+
+    FaceSetFeatureClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int userId,
+            @NonNull String owner, int sensorId, int feature, boolean enabled,
+            byte[] hardwareAuthToken, int faceId) {
+        super(context, token, listener, userId, owner, 0 /* cookie */, sensorId,
+                BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN,
+                BiometricsProtoEnums.CLIENT_UNKNOWN);
+        mFeature = feature;
+        mEnabled = enabled;
+        mFaceId = faceId;
+
+        mHardwareAuthToken = new ArrayList<>();
+        for (byte b : hardwareAuthToken) {
+            mHardwareAuthToken.add(b);
+        }
+    }
+
+    @Override
+    public void unableToStart() {
+        try {
+            getListener().onFeatureSet(false /* success */, mFeature);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to send error", e);
+        }
+    }
+
+    @Override
+    public void start(@NonNull IBiometricsFace daemon, @NonNull FinishCallback finishCallback) {
+        super.start(daemon, finishCallback);
+
+        startHalOperation();
+        mFinishCallback.onClientFinished(this);
+    }
+
+    @Override
+    protected void startHalOperation() {
+        try {
+            final int result = mDaemon.setFeature(mFeature, mEnabled, mHardwareAuthToken, mFaceId);
+            getListener().onFeatureSet(result == Status.OK, mFeature);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Unable to set feature: " + mFeature + " to enabled: " + mEnabled, e);
+        }
+    }
+
+    @Override
+    protected void stopHalOperation() {
+        // Not supported for SetFeature
+    }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
index d4e9882..ba89d51 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticationClient.java
@@ -41,26 +41,22 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintAuthenticationClient extends AuthenticationClient {
+class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFingerprint> {
 
     private static final String TAG = "Biometrics/FingerprintAuthClient";
 
-    private final IBiometricsFingerprint mDaemon;
     private final LockoutFrameworkImpl mLockoutFrameworkImpl;
 
-    FingerprintAuthenticationClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBiometricsFingerprint daemon,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            int targetUserId, long operationId, boolean restricted, @NonNull String owner,
-            int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
-            @Nullable Surface surface, int statsClient,
+    FingerprintAuthenticationClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
+            boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
+            int sensorId, boolean isStrongBiometric, @Nullable Surface surface, int statsClient,
             @NonNull TaskStackListener taskStackListener,
             @NonNull LockoutFrameworkImpl lockoutTracker) {
-        super(finishCallback, context, token, listener, targetUserId, operationId, restricted,
+        super(context, token, listener, targetUserId, operationId, restricted,
                 owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
                 BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
                 lockoutTracker);
-        mDaemon = daemon;
         mLockoutFrameworkImpl = lockoutTracker;
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
index abfbeda..3418c46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintAuthenticator.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 
 import com.android.server.biometrics.SensorConfig;
+import com.android.server.biometrics.sensors.LockoutTracker;
 
 /**
  * Shim that converts IFingerprintService into a common reusable IBiometricAuthenticator interface.
@@ -68,6 +69,12 @@
     }
 
     @Override
+    public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
+            throws RemoteException {
+        return mFingerprintService.getLockoutModeForUser(userId);
+    }
+
+    @Override
     public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
         mFingerprintService.resetLockout(userId, hardwareAuthToken);
     }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
index e5d2887..34681c3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -33,20 +34,16 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-public class FingerprintEnrollClient extends EnrollClient {
+public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint> {
 
     private static final String TAG = "FingerprintEnrollClient";
-    private final IBiometricsFingerprint mDaemon;
 
-    FingerprintEnrollClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFingerprint daemon, @NonNull IBinder token,
+    FingerprintEnrollClient(@NonNull Context context, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int userId,
-            @NonNull byte[] hardwareAuthToken, boolean restricted, @NonNull String owner,
-            @NonNull BiometricUtils utils, int timeoutSec, int statsModality,
-            int sensorId, boolean shouldVibrate) {
-        super(finishCallback, context, token, listener, userId, hardwareAuthToken, restricted,
-                owner, utils, timeoutSec, statsModality, sensorId, shouldVibrate);
-        mDaemon = daemon;
+            @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
+            int timeoutSec, int sensorId) {
+        super(context, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, true /* shouldVibrate */);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
index 5ff49f3..2fa4333 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintGenerateChallengeClient.java
@@ -31,18 +31,14 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-public class FingerprintGenerateChallengeClient extends GenerateChallengeClient {
+public class FingerprintGenerateChallengeClient
+        extends GenerateChallengeClient<IBiometricsFingerprint> {
 
     private static final String TAG = "FingerprintGenerateChallengeClient";
 
-    private final IBiometricsFingerprint mDaemon;
-
-    FingerprintGenerateChallengeClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBiometricsFingerprint daemon,
-            @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener,
-            @NonNull String owner, int sensorId) {
-        super(finishCallback, context, token, listener, owner, sensorId);
-        mDaemon = daemon;
+    FingerprintGenerateChallengeClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) {
+        super(context, token, listener, owner, sensorId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
index 1688bde..71e7670 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalCleanupClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.os.IBinder;
 
@@ -34,36 +35,31 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintInternalCleanupClient extends InternalCleanupClient {
-    private final IBiometricsFingerprint mDaemon;
+class FingerprintInternalCleanupClient extends InternalCleanupClient<IBiometricsFingerprint> {
 
-    FingerprintInternalCleanupClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBiometricsFingerprint daemon, int userId,
-            boolean restricted, @NonNull String owner, int sensorId, int statsModality,
-            @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
+    FingerprintInternalCleanupClient(@NonNull Context context,int userId, @NonNull String owner,
+            int sensorId, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
             @NonNull BiometricUtils utils) {
-        super(finishCallback, context, userId, restricted, owner, sensorId, statsModality,
+        super(context, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT,
                 enrolledList, utils);
-        mDaemon = daemon;
     }
 
     @Override
-    protected InternalEnumerateClient getEnumerateClient(FinishCallback finishCallback,
-            Context context, IBinder token, int userId, boolean restricted, String owner,
+    protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient(
+            Context context, IBinder token, int userId, String owner,
             List<? extends BiometricAuthenticator.Identifier> enrolledList, BiometricUtils utils,
-            int sensorId, int statsModality) {
-        return new FingerprintInternalEnumerateClient(finishCallback, context, mDaemon, token,
-                userId, restricted, owner, enrolledList, utils, sensorId, statsModality);
+            int sensorId) {
+        return new FingerprintInternalEnumerateClient(context, token, userId, owner, enrolledList,
+                utils, sensorId);
     }
 
     @Override
-    protected RemovalClient getRemovalClient(FinishCallback finishCallback, Context context,
-            IBinder token, int biometricId, int userId, boolean restricted, String owner,
-            BiometricUtils utils, int sensorId, int statsModality) {
+    protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context, IBinder token,
+            int biometricId, int userId, String owner, BiometricUtils utils, int sensorId) {
         // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove)
         // is all done internally.
-        return new FingerprintRemovalClient(finishCallback, context, mDaemon, token,
-                null /* ClientMonitorCallbackConverter */, biometricId, userId, restricted,
-                owner, utils, sensorId, statsModality);
+        return new FingerprintRemovalClient(context, token,
+                null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils,
+                sensorId);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
index 2b34f08..ba412e3 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintInternalEnumerateClient.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -34,19 +35,15 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintInternalEnumerateClient extends InternalEnumerateClient {
+class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFingerprint> {
     private static final String TAG = "FingerprintInternalEnumerateClient";
 
-    private final IBiometricsFingerprint mDaemon;
-
-    FingerprintInternalEnumerateClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBiometricsFingerprint daemon,
-            @NonNull IBinder token, int userId, boolean restricted, @NonNull String owner,
+    FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull IBinder token, int userId,
+            @NonNull String owner,
             @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList,
-            @NonNull BiometricUtils utils, int sensorId, int statsModality) {
-        super(finishCallback, context, token, userId, restricted, owner, enrolledList, utils,
-                sensorId, statsModality);
-        mDaemon = daemon;
+            @NonNull BiometricUtils utils, int sensorId) {
+        super(context, token, userId, owner, enrolledList, utils, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
index d284dc6..6d7e761 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRemovalClient.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.hardware.biometrics.BiometricsProtoEnums;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -32,19 +33,14 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-class FingerprintRemovalClient extends RemovalClient {
+class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> {
     private static final String TAG = "FingerprintRemovalClient";
 
-    private final IBiometricsFingerprint mDaemon;
-
-    FingerprintRemovalClient(@NonNull FinishCallback finishCallback, @NonNull Context context,
-            @NonNull IBiometricsFingerprint daemon, @NonNull IBinder token,
+    FingerprintRemovalClient(@NonNull Context context, @NonNull IBinder token,
             @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId,
-            boolean restricted, @NonNull String owner, @NonNull BiometricUtils utils, int sensorId,
-            int statsModality) {
-        super(finishCallback, context, token, listener, biometricId, userId, restricted, owner,
-                utils, sensorId, statsModality);
-        mDaemon = daemon;
+            @NonNull String owner, @NonNull BiometricUtils utils, int sensorId) {
+        super(context, token, listener, biometricId, userId, owner, utils, sensorId,
+                BiometricsProtoEnums.MODALITY_FINGERPRINT);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
index 99e9d54..ccbea31d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintRevokeChallengeClient.java
@@ -30,17 +30,14 @@
  * {@link android.hardware.biometrics.fingerprint.V2_1} and
  * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces.
  */
-public class FingerprintRevokeChallengeClient extends RevokeChallengeClient {
+public class FingerprintRevokeChallengeClient
+        extends RevokeChallengeClient<IBiometricsFingerprint> {
 
     private static final String TAG = "FingerprintRevokeChallengeClient";
 
-    private final IBiometricsFingerprint mDaemon;
-
-    FingerprintRevokeChallengeClient(@NonNull FinishCallback finishCallback,
-            @NonNull Context context, @NonNull IBiometricsFingerprint daemon,
-            @NonNull IBinder token, @NonNull String owner, int sensorId) {
-        super(finishCallback, context, token, owner, sensorId);
-        mDaemon = daemon;
+    FingerprintRevokeChallengeClient(@NonNull Context context, @NonNull IBinder token,
+            @NonNull String owner, int sensorId) {
+        super(context, token, owner, sensorId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index b4478cb..d2a25db 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -40,6 +40,7 @@
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.IUdfpsOverlayController;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Environment;
@@ -90,7 +91,7 @@
  *
  * @hide
  */
-public class FingerprintService extends BiometricServiceBase {
+public class FingerprintService extends BiometricServiceBase<IBiometricsFingerprint> {
 
     protected static final String TAG = "FingerprintService";
     private static final boolean DEBUG = true;
@@ -108,19 +109,12 @@
 
         @Override // Binder call
         public void generateChallenge(IBinder token, IFingerprintServiceReceiver receiver,
-                String opPackageName) throws RemoteException {
+                String opPackageName) {
             checkPermission(MANAGE_FINGERPRINT);
 
-            final IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to generateChallenge, daemon null");
-                receiver.onChallengeGenerated(0L);
-                return;
-            }
-
             final GenerateChallengeClient client = new FingerprintGenerateChallengeClient(
-                    mClientFinishCallback, getContext(), daemon, token,
-                    new ClientMonitorCallbackConverter(receiver), opPackageName, getSensorId());
+                    getContext(), token, new ClientMonitorCallbackConverter(receiver),
+                    opPackageName, getSensorId());
             generateChallengeInternal(client);
         }
 
@@ -128,37 +122,21 @@
         public void revokeChallenge(IBinder token, String owner) {
             checkPermission(MANAGE_FINGERPRINT);
 
-            final IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "startPostEnroll: no fingerprint HAL!");
-                return;
-            }
-
-            final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(
-                    mClientFinishCallback, getContext(), daemon, token, owner, getSensorId());
+            final RevokeChallengeClient client = new FingerprintRevokeChallengeClient(getContext(),
+                    token, owner, getSensorId());
             revokeChallengeInternal(client);
         }
 
         @Override // Binder call
         public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
-                final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName, Surface surface) throws RemoteException {
+                final IFingerprintServiceReceiver receiver, final String opPackageName,
+                final Surface surface) {
             checkPermission(MANAGE_FINGERPRINT);
-            updateActiveGroup(userId, opPackageName);
+            updateActiveGroup(userId);
 
-            final IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to enroll, daemon null");
-                receiver.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
-
-            final boolean restricted = isRestricted();
-            final EnrollClient client = new FingerprintEnrollClient(mClientFinishCallback,
-                    getContext(), daemon, token, new ClientMonitorCallbackConverter(receiver),
-                    userId, cryptoToken, restricted, opPackageName, getBiometricUtils(),
-                    ENROLL_TIMEOUT_SEC, statsModality(), getSensorId(), true /* shouldVibrate */);
+            final EnrollClient client = new FingerprintEnrollClient(getContext(), token,
+                    new ClientMonitorCallbackConverter(receiver), userId, cryptoToken,
+                    opPackageName, getBiometricUtils(), ENROLL_TIMEOUT_SEC, getSensorId());
 
             enrollInternal(client, userId);
         }
@@ -171,17 +149,9 @@
 
         @Override // Binder call
         public void authenticate(final IBinder token, final long opId, final int userId,
-                final IFingerprintServiceReceiver receiver, final int flags,
-                final String opPackageName, Surface surface) throws RemoteException {
-            updateActiveGroup(userId, opPackageName);
-
-            final IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to authenticate, daemon null");
-                receiver.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
+                final IFingerprintServiceReceiver receiver, final String opPackageName,
+                final Surface surface) {
+            updateActiveGroup(userId);
 
             final boolean isStrongBiometric;
             final long ident = Binder.clearCallingIdentity();
@@ -194,9 +164,8 @@
             final boolean restricted = isRestricted();
             final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
                     : BiometricsProtoEnums.CLIENT_FINGERPRINT_MANAGER;
-            final AuthenticationClient client = new FingerprintAuthenticationClient(
-                    mClientFinishCallback, getContext(), daemon, token,
-                    new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
+            final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
+                    token, new ClientMonitorCallbackConverter(receiver), userId, opId, restricted,
                     opPackageName, 0 /* cookie */, false /* requireConfirmation */, getSensorId(),
                     isStrongBiometric, surface, statsClient, mTaskStackListener, mLockoutTracker);
             authenticateInternal(client, opPackageName);
@@ -208,21 +177,11 @@
                 int cookie, int callingUid, int callingPid, int callingUserId,
                 Surface surface) throws RemoteException {
             checkPermission(MANAGE_BIOMETRIC);
-            updateActiveGroup(userId, opPackageName);
-
-            final IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to prepare for authentication, daemon null");
-                sensorReceiver.onError(getSensorId(), cookie,
-                        BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
+            updateActiveGroup(userId);
 
             final boolean restricted = true; // BiometricPrompt is always restricted
-            final AuthenticationClient client = new FingerprintAuthenticationClient(
-                    mClientFinishCallback, getContext(), daemon, token,
-                    new ClientMonitorCallbackConverter(sensorReceiver), userId, opId,
+            final AuthenticationClient client = new FingerprintAuthenticationClient(getContext(),
+                    token, new ClientMonitorCallbackConverter(sensorReceiver), userId, opId,
                     restricted, opPackageName, cookie, false /* requireConfirmation */,
                     getSensorId(), isStrongBiometric(), surface,
                     BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, mTaskStackListener,
@@ -257,26 +216,16 @@
                 final IFingerprintServiceReceiver receiver, final String opPackageName)
                 throws RemoteException {
             checkPermission(MANAGE_FINGERPRINT);
-            updateActiveGroup(userId, opPackageName);
+            updateActiveGroup(userId);
 
             if (token == null) {
                 Slog.w(TAG, "remove(): token is null");
                 return;
             }
 
-            final IBiometricsFingerprint daemon = getFingerprintDaemon();
-            if (daemon == null) {
-                Slog.e(TAG, "Unable to remove, daemon null");
-                receiver.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        0 /* vendorCode */);
-                return;
-            }
-
-            final boolean restricted = isRestricted();
-            final RemovalClient client = new FingerprintRemovalClient(mClientFinishCallback,
-                    getContext(), daemon, token, new ClientMonitorCallbackConverter(receiver),
-                    fingerId, userId, restricted, opPackageName, getBiometricUtils(),
-                    getSensorId(), statsModality());
+            final RemovalClient client = new FingerprintRemovalClient(getContext(), token,
+                    new ClientMonitorCallbackConverter(receiver), fingerId, userId, opPackageName,
+                    getBiometricUtils(), getSensorId());
             removeInternal(client);
         }
 
@@ -365,6 +314,12 @@
         }
 
         @Override // Binder call
+        public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+            checkPermission(USE_BIOMETRIC_INTERNAL);
+            return mLockoutTracker.getLockoutModeForUser(userId);
+        }
+
+        @Override // Binder call
         public long getAuthenticatorId(int callingUserId) {
             checkPermission(USE_BIOMETRIC_INTERNAL);
             return FingerprintService.this.getAuthenticatorId(callingUserId);
@@ -409,11 +364,109 @@
             checkPermission(USE_BIOMETRIC_INTERNAL);
             initializeConfigurationInternal(sensorId);
         }
+
+        @Override
+        public void onFingerDown(int x, int y, float minor, float major) {
+            checkPermission(USE_BIOMETRIC_INTERNAL);
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.e(TAG, "onFingerDown | daemon is null");
+            } else {
+                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+                        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+                                daemon);
+                if (extension == null) {
+                    Slog.v(TAG, "onFingerDown | failed to cast the HIDL to V2_3");
+                } else {
+                    try {
+                        extension.onFingerDown(x, y, minor, major);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "onFingerDown | RemoteException: ", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onFingerUp() {
+            checkPermission(USE_BIOMETRIC_INTERNAL);
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.e(TAG, "onFingerUp | daemon is null");
+            } else {
+                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+                        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+                                daemon);
+                if (extension == null) {
+                    Slog.v(TAG, "onFingerUp | failed to cast the HIDL to V2_3");
+                } else {
+                    try {
+                        extension.onFingerUp();
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "onFingerUp | RemoteException: ", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean isUdfps(int sensorId) {
+            checkPermission(USE_BIOMETRIC_INTERNAL);
+            IBiometricsFingerprint daemon = getFingerprintDaemon();
+            if (daemon == null) {
+                Slog.e(TAG, "isUdfps | daemon is null");
+            } else {
+                android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
+                        android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
+                                daemon);
+                if (extension == null) {
+                    Slog.v(TAG, "isUdfps | failed to cast the HIDL to V2_3");
+                } else {
+                    try {
+                        return extension.isUdfps(sensorId);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "isUdfps | RemoteException: ", e);
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void showUdfpsOverlay() {
+            if (mUdfpsOverlayController == null) {
+                Slog.e(TAG, "showUdfpsOverlay | mUdfpsOverlayController is null");
+                return;
+            }
+            try {
+                mUdfpsOverlayController.showUdfpsOverlay();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "showUdfpsOverlay | RemoteException: ", e);
+            }
+        }
+
+        @Override
+        public void hideUdfpsOverlay() {
+            if (mUdfpsOverlayController == null) {
+                Slog.e(TAG, "hideUdfpsOverlay | mUdfpsOverlayController is null");
+                return;
+            }
+            try {
+                mUdfpsOverlayController.hideUdfpsOverlay();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "hideUdfpsOverlay | RemoteException: ", e);
+            }
+        }
+
+        public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+            mUdfpsOverlayController = controller;
+        }
     }
 
     private final LockoutFrameworkImpl mLockoutTracker;
     private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks =
             new CopyOnWriteArrayList<>();
+    private IUdfpsOverlayController mUdfpsOverlayController;
 
     @GuardedBy("this")
     private IBiometricsFingerprint mDaemon;
@@ -513,6 +566,11 @@
     }
 
     @Override
+    protected IBiometricsFingerprint getDaemon() {
+        return getFingerprintDaemon();
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return FingerprintUtils.getInstance();
     }
@@ -536,12 +594,11 @@
     }
 
     @Override
-    protected void updateActiveGroup(int userId, String clientPackage) {
+    protected void updateActiveGroup(int userId) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
 
         if (daemon != null) {
             try {
-                userId = getUserOrWorkProfileId(clientPackage, userId);
                 if (userId != mCurrentUserId) {
                     int firstSdkInt = Build.VERSION.FIRST_SDK_INT;
                     if (firstSdkInt < Build.VERSION_CODES.BASE) {
@@ -648,18 +705,10 @@
 
     @Override
     protected void doTemplateCleanupForUser(int userId) {
-        final IBiometricsFingerprint daemon = getFingerprintDaemon();
-        if (daemon == null) {
-            Slog.e(TAG, "daemon null, skipping template cleanup");
-            return;
-        }
-
-        final boolean restricted = !hasPermission(getManageBiometricPermission());
         final List<? extends BiometricAuthenticator.Identifier> enrolledList =
                 getEnrolledTemplates(userId);
         final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(
-                mClientFinishCallback, getContext(), daemon, userId, restricted,
-                getContext().getOpPackageName(), getSensorId(), statsModality(), enrolledList,
+                getContext(), userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
                 getBiometricUtils());
         cleanupInternal(client);
     }
@@ -694,7 +743,7 @@
             if (halId != 0) {
                 loadAuthenticatorIds();
                 final int userId = ActivityManager.getCurrentUser();
-                updateActiveGroup(userId, null);
+                updateActiveGroup(userId);
                 doTemplateCleanupForUser(userId);
             } else {
                 Slog.w(TAG, "Failed to open Fingerprint HAL!");
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
index a2e5137..9e04057 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisAuthenticator.java
@@ -23,6 +23,7 @@
 import android.os.RemoteException;
 
 import com.android.server.biometrics.SensorConfig;
+import com.android.server.biometrics.sensors.LockoutTracker;
 
 /**
  * TODO(b/141025588): Add JavaDoc.
@@ -63,6 +64,12 @@
     }
 
     @Override
+    public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId)
+            throws RemoteException {
+        return LockoutTracker.LOCKOUT_NONE;
+    }
+
+    @Override
     public void resetLockout(int userId, byte[] hardwareAuthToken) throws RemoteException {
     }
 
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
index 1e7b606..023ed46 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
@@ -85,6 +85,11 @@
     }
 
     @Override
+    protected Object getDaemon() {
+        return null;
+    }
+
+    @Override
     protected BiometricUtils getBiometricUtils() {
         return null;
     }
@@ -95,7 +100,7 @@
     }
 
     @Override
-    protected void updateActiveGroup(int userId, String clientPackage) {
+    protected void updateActiveGroup(int userId) {
 
     }
 
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index e715890..f812a05 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -73,6 +73,8 @@
     @GuardedBy("mProxyLock")
     private boolean mDefaultProxyEnabled = true;
 
+    private final Handler mConnectivityServiceHandler;
+
     // The object responsible for Proxy Auto Configuration (PAC).
     @NonNull
     private final PacManager mPacManager;
@@ -80,6 +82,7 @@
     public ProxyTracker(@NonNull final Context context,
             @NonNull final Handler connectivityServiceInternalHandler, final int pacChangedEvent) {
         mContext = context;
+        mConnectivityServiceHandler = connectivityServiceInternalHandler;
         mPacManager = new PacManager(context, connectivityServiceInternalHandler, pacChangedEvent);
     }
 
@@ -149,6 +152,9 @@
      * Read the global proxy settings and cache them in memory.
      */
     public void loadGlobalProxy() {
+        if (loadDeprecatedGlobalHttpProxy()) {
+            return;
+        }
         ContentResolver res = mContext.getContentResolver();
         String host = Settings.Global.getString(res, GLOBAL_HTTP_PROXY_HOST);
         int port = Settings.Global.getInt(res, GLOBAL_HTTP_PROXY_PORT, 0);
@@ -169,20 +175,24 @@
             synchronized (mProxyLock) {
                 mGlobalProxy = proxyProperties;
             }
+
+            if (!TextUtils.isEmpty(pacFileUrl)) {
+                mConnectivityServiceHandler.post(
+                        () -> mPacManager.setCurrentProxyScriptUrl(proxyProperties));
+            }
         }
-        loadDeprecatedGlobalHttpProxy();
-        // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
     }
 
     /**
      * Read the global proxy from the deprecated Settings.Global.HTTP_PROXY setting and apply it.
+     * Returns {@code true} when global proxy was set successfully from deprecated setting.
      */
-    public void loadDeprecatedGlobalHttpProxy() {
+    public boolean loadDeprecatedGlobalHttpProxy() {
         final String proxy = Settings.Global.getString(mContext.getContentResolver(), HTTP_PROXY);
         if (!TextUtils.isEmpty(proxy)) {
             String data[] = proxy.split(":");
             if (data.length == 0) {
-                return;
+                return false;
             }
 
             final String proxyHost = data[0];
@@ -191,12 +201,14 @@
                 try {
                     proxyPort = Integer.parseInt(data[1]);
                 } catch (NumberFormatException e) {
-                    return;
+                    return false;
                 }
             }
             final ProxyInfo p = new ProxyInfo(proxyHost, proxyPort, "");
             setGlobalProxy(p);
+            return true;
         }
+        return false;
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 566af3c..5ff40d0 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -450,7 +450,7 @@
 
             // Determine whether the active color mode is still there.
             if (!mSupportedColorModes.contains(mActiveColorMode)) {
-                if (mActiveColorMode != 0) {
+                if (mActiveColorMode != Display.COLOR_MODE_DEFAULT) {
                     Slog.w(TAG, "Active color mode no longer available, reverting"
                             + " to default mode.");
                     mActiveColorMode = Display.COLOR_MODE_DEFAULT;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9acb475..15e8a92 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4047,7 +4047,9 @@
                     // Send it to window manager to hide IME from IME target window.
                     // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
                     // actual IME target.
-                    mWindowManagerInternal.hideIme(mHideRequestWindowMap.get(windowToken));
+                    mWindowManagerInternal.hideIme(
+                            mHideRequestWindowMap.get(windowToken),
+                            mCurClient.selfReportedDisplayId);
                 }
             } else {
                 // Send to window manager to show IME after IME layout finishes.
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9a6ffd0..5c1d182 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -20,10 +20,19 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationManager.EXTRA_LOCATION_ENABLED;
+import static android.location.LocationManager.EXTRA_PROVIDER_ENABLED;
+import static android.location.LocationManager.EXTRA_PROVIDER_NAME;
 import static android.location.LocationManager.FUSED_PROVIDER;
 import static android.location.LocationManager.GPS_PROVIDER;
+import static android.location.LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION;
+import static android.location.LocationManager.KEY_LOCATION_CHANGED;
+import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
+import static android.location.LocationManager.MODE_CHANGED_ACTION;
 import static android.location.LocationManager.NETWORK_PROVIDER;
 import static android.location.LocationManager.PASSIVE_PROVIDER;
+import static android.location.LocationManager.PROVIDERS_CHANGED_ACTION;
+import static android.location.LocationManager.invalidateLocalLocationEnabledCaches;
 import static android.os.PowerManager.locationPowerSaveModeToString;
 
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -55,6 +64,7 @@
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
 import android.location.IGpsGeofenceHardware;
+import android.location.ILocationCallback;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
 import android.location.Location;
@@ -71,6 +81,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
+import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
@@ -134,6 +145,7 @@
 import java.util.Objects;
 import java.util.TreeMap;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
 
 /**
  * The service class that manages LocationProviders and issues location
@@ -162,7 +174,7 @@
             publishBinderService(Context.LOCATION_SERVICE, mService);
 
             // client caching behavior is only enabled after seeing the first invalidate
-            LocationManager.invalidateLocalLocationEnabledCaches();
+            invalidateLocalLocationEnabledCaches();
             // disable caching for our own process
             Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
                     .disableLocalLocationEnabledCaches();
@@ -242,9 +254,9 @@
     // time
     private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
 
-    private static final String ATTRIBUTION_TAG = "LocationService";
+    private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30000;
 
-    private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
+    private static final String ATTRIBUTION_TAG = "LocationService";
 
     private final Object mLock = new Object();
 
@@ -475,14 +487,14 @@
 
     private void onLocationModeChanged(int userId) {
         boolean enabled = mSettingsHelper.isLocationEnabled(userId);
-        LocationManager.invalidateLocalLocationEnabledCaches();
+        invalidateLocalLocationEnabledCaches();
 
         if (D) {
             Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
         }
 
-        Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION)
-                .putExtra(LocationManager.EXTRA_LOCATION_ENABLED, enabled)
+        Intent intent = new Intent(MODE_CHANGED_ACTION)
+                .putExtra(EXTRA_LOCATION_ENABLED, enabled)
                 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
@@ -630,7 +642,8 @@
                 manager = new LocationProviderManager(name);
                 mProviderManagers.add(manager);
             }
-            manager.setMockProvider(new MockProvider(properties));
+            manager.setMockProvider(
+                    new MockProvider(properties, CallerIdentity.fromContext(mContext)));
         }
     }
 
@@ -1007,9 +1020,9 @@
             if (wasEnabled != null) {
                 // fused and passive provider never get public updates for legacy reasons
                 if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
-                    Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION)
-                            .putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName)
-                            .putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, enabled)
+                    Intent intent = new Intent(PROVIDERS_CHANGED_ACTION)
+                            .putExtra(EXTRA_PROVIDER_NAME, mName)
+                            .putExtra(EXTRA_PROVIDER_ENABLED, enabled)
                             .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                             .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                     mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
@@ -1218,7 +1231,7 @@
                 long identity = Binder.clearCallingIdentity();
                 try {
                     // Send an intent to notify that a high power request has been added/removed.
-                    Intent intent = new Intent(LocationManager.HIGH_POWER_REQUEST_CHANGE_ACTION);
+                    Intent intent = new Intent(HIGH_POWER_REQUEST_CHANGE_ACTION);
                     mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
@@ -1271,7 +1284,14 @@
                 LocationRequest locationRequest) {
             if (mListener != null) {
                 try {
-                    mListener.onLocationChanged(new Location(location));
+                    mListener.onLocationChanged(new Location(location), new IRemoteCallback.Stub() {
+                        @Override
+                        public void sendResult(Bundle data) {
+                            synchronized (mLock) {
+                                decrementPendingBroadcastsLocked();
+                            }
+                        }
+                    });
                     // call this after broadcasting so we do not increment
                     // if we throw an exception.
                     incrementPendingBroadcastsLocked();
@@ -1280,8 +1300,7 @@
                 }
             } else {
                 Intent locationChanged = new Intent();
-                locationChanged.putExtra(LocationManager.KEY_LOCATION_CHANGED,
-                        new Location(location));
+                locationChanged.putExtra(KEY_LOCATION_CHANGED, new Location(location));
                 try {
                     mPendingIntent.send(mContext, 0, locationChanged, this, mHandler,
                             LocationPermissions.asPermission(
@@ -1306,29 +1325,19 @@
 
             if (mListener != null) {
                 try {
-                    if (enabled) {
-                        mListener.onProviderEnabled(provider);
-                    } else {
-                        mListener.onProviderDisabled(provider);
-                    }
-                    // call this after broadcasting so we do not increment
-                    // if we throw an exception.
-                    incrementPendingBroadcastsLocked();
+                    mListener.onProviderEnabledChanged(provider, enabled);
                 } catch (RemoteException e) {
                     return false;
                 }
             } else {
                 Intent providerIntent = new Intent();
-                providerIntent.putExtra(LocationManager.KEY_PROVIDER_ENABLED, enabled);
+                providerIntent.putExtra(KEY_PROVIDER_ENABLED, enabled);
                 try {
-                    mPendingIntent.send(mContext, 0, providerIntent, this, mHandler,
+                    mPendingIntent.send(mContext, 0, providerIntent, null, mHandler,
                             LocationPermissions.asPermission(
                                     locationRequest.isCoarse() ? PERMISSION_COARSE
                                             : PERMISSION_FINE),
                             PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
-                    // call this after broadcasting so we do not increment
-                    // if we throw an exception.
-                    incrementPendingBroadcastsLocked();
                 } catch (PendingIntent.CanceledException e) {
                     return false;
                 }
@@ -1336,16 +1345,6 @@
             return true;
         }
 
-        public void callRemovedLocked() {
-            if (mListener != null) {
-                try {
-                    mListener.onRemoved();
-                } catch (RemoteException e) {
-                    // doesn't matter
-                }
-            }
-        }
-
         @Override
         public void binderDied() {
             synchronized (mLock) {
@@ -1406,20 +1405,6 @@
     }
 
     @Override
-    public void locationCallbackFinished(ILocationListener listener) {
-        //Do not use getReceiverLocked here as that will add the ILocationListener to
-        //the receiver list if it is not found.  If it is not found then the
-        //LocationListener was removed when it had a pending broadcast and should
-        //not be added back.
-        synchronized (mLock) {
-            Receiver receiver = mReceivers.get(listener.asBinder());
-            if (receiver != null) {
-                receiver.decrementPendingBroadcastsLocked();
-            }
-        }
-    }
-
-    @Override
     public int getGnssYearOfHardware() {
         return mGnssManagerService == null ? 0 : mGnssManagerService.getGnssYearOfHardware();
     }
@@ -2121,10 +2106,6 @@
     @Override
     public Location getLastLocation(LocationRequest request, String packageName,
             String attributionTag) {
-        if (request == null) {
-            request = DEFAULT_LOCATION_REQUEST;
-        }
-
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,   attributionTag);
         int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
@@ -2161,42 +2142,78 @@
     }
 
     @Override
-    public boolean getCurrentLocation(LocationRequest locationRequest,
-            ICancellationSignal remoteCancellationSignal, ILocationListener listener,
+    public void getCurrentLocation(LocationRequest request,
+            ICancellationSignal remoteCancellationSignal, ILocationCallback callback,
             String packageName, String attributionTag, String listenerId) {
-        // side effect of validating locationRequest and packageName
-        Location lastLocation = getLastLocation(locationRequest, packageName, attributionTag);
+        // unsafe is ok because app ops will verify the package name
+        CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag,
+                listenerId);
+        int permissionLevel = LocationPermissions.getCallingOrSelfPermissionLevel(mContext);
+        LocationPermissions.enforceLocationPermission(Binder.getCallingUid(), permissionLevel,
+                PERMISSION_COARSE);
+
+        request = createSanitizedRequest(request, false, permissionLevel);
+        request.setNumUpdates(1);
+        if (request.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+            request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+        }
+
+        GetCurrentLocationTransport transport = new GetCurrentLocationTransport(callback);
+
+        if (mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
+                identity.getPackageName())) {
+            transport.deliverResult(null);
+            return;
+        }
+        if (!mUserInfoHelper.isCurrentUserId(identity.getUserId())) {
+            transport.deliverResult(null);
+            return;
+        }
+
+        Location lastLocation;
+        synchronized (mLock) {
+            LocationProviderManager manager = getLocationProviderManager(request.getProvider());
+            if (manager == null) {
+                transport.deliverResult(null);
+                return;
+            }
+            if (!manager.isEnabled(identity.getUserId()) && !request.isLocationSettingsIgnored()) {
+                transport.deliverResult(null);
+                return;
+            }
+
+            lastLocation = manager.getLastLocation(identity.getUserId(), permissionLevel);
+        }
+
         if (lastLocation != null) {
             long locationAgeMs = NANOSECONDS.toMillis(
                     SystemClock.elapsedRealtimeNanos() - lastLocation.getElapsedRealtimeNanos());
 
             if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
-                try {
-                    listener.onLocationChanged(lastLocation);
-                    return true;
-                } catch (RemoteException e) {
-                    Log.w(TAG, e);
-                    return false;
+                // appops check should always be right before delivery
+                if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+                    transport.deliverResult(lastLocation);
+                } else {
+                    transport.deliverResult(null);
                 }
+                return;
             }
 
             if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())) {
                 if (locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
                     // not allowed to request new locations, so we can't return anything
-                    return false;
+                    transport.deliverResult(null);
+                    return;
                 }
             }
         }
 
-        registerLocationListener(locationRequest, listener, packageName, attributionTag,
-                listenerId);
+        registerLocationListener(request, transport, packageName, attributionTag, listenerId);
         CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
                 remoteCancellationSignal);
         if (cancellationSignal != null) {
-            cancellationSignal.setOnCancelListener(
-                    () -> unregisterLocationListener(listener));
+            cancellationSignal.setOnCancelListener(() -> unregisterLocationListener(transport));
         }
-        return true;
     }
 
     @Override
@@ -2435,7 +2452,7 @@
 
         mContext.enforceCallingOrSelfPermission(permission.WRITE_SECURE_SETTINGS, null);
 
-        LocationManager.invalidateLocalLocationEnabledCaches();
+        invalidateLocalLocationEnabledCaches();
         mSettingsHelper.setLocationEnabled(enabled, userId);
     }
 
@@ -2560,8 +2577,6 @@
 
             // track expired records
             if (r.mRealRequest.getNumUpdates() <= 0 || r.mExpirationRealtimeMs < now) {
-                // notify the client it can remove this listener
-                r.mReceiver.callRemovedLocked();
                 if (deadUpdateRecords == null) {
                     deadUpdateRecords = new ArrayList<>();
                 }
@@ -2652,7 +2667,7 @@
                 mProviderManagers.add(manager);
             }
 
-            manager.setMockProvider(new MockProvider(properties));
+            manager.setMockProvider(new MockProvider(properties, identity));
         }
     }
 
@@ -2839,6 +2854,47 @@
         }
     }
 
+    private class GetCurrentLocationTransport extends ILocationListener.Stub {
+
+        private final Executor mExecutor;
+        private final ILocationCallback mCallback;
+
+        GetCurrentLocationTransport(ILocationCallback callback) {
+            mExecutor = FgThread.getExecutor();
+            mCallback = callback;
+        }
+
+        @Override
+        public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+            mExecutor.execute(() -> {
+                deliverResult(location);
+                try {
+                    onCompleteCallback.sendResult(null);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            });
+            unregisterLocationListener(this);
+        }
+
+        @Override
+        public void onProviderEnabledChanged(String provider, boolean enabled)
+                throws RemoteException {
+            if (!enabled) {
+                deliverResult(null);
+                unregisterLocationListener(this);
+            }
+        }
+
+        public void deliverResult(@Nullable Location location) {
+            try {
+                mCallback.onLocation(location);
+            } catch (RemoteException e) {
+                // do nothing
+            }
+        }
+    }
+
     private class LocalService extends LocationManagerInternal {
 
         LocalService() {}
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 160e641..c5bd5e6 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -20,6 +20,7 @@
 
 import android.annotation.Nullable;
 import android.location.Location;
+import android.location.util.identity.CallerIdentity;
 import android.os.Bundle;
 
 import com.android.internal.location.ProviderProperties;
@@ -37,10 +38,11 @@
 
     @Nullable private Location mLocation;
 
-    public MockProvider(ProviderProperties properties) {
+    public MockProvider(ProviderProperties properties, CallerIdentity identity) {
         // using a direct executor is ok because this class has no locks that could deadlock
         super(DIRECT_EXECUTOR);
         setProperties(properties);
+        setIdentity(identity);
     }
 
     /** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 33e2bfa..c855a12 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -112,10 +112,9 @@
         }
 
         @Override
-        protected boolean onPendingIntentRegister(Object key) {
+        protected void onPendingIntentListenerRegister() {
             mGeofenceState = STATE_UNKNOWN;
             mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
-            return true;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 564b90d..53e660a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -89,10 +89,9 @@
         }
 
         @Override
-        protected boolean onBinderRegister(Object key) {
+        protected void onBinderListenerRegister() {
             mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
             mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
-            return true;
         }
 
         boolean onAppOpsChanged(String packageName) {
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index c853cee..bd8bce8 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -49,41 +49,34 @@
         super(tag, request, callerIdentity, listener);
     }
 
-    /**
-     * May be overridden in place of {@link #onRegister(Object)}. Should return true if registration
-     * is successful, and false otherwise.
-     */
-    protected boolean onBinderRegister(Object key) {
-        return true;
-    }
-
-    /**
-     * May be overridden in place of {@link #onUnregister()}.
-     */
-    protected void onBinderUnregister(Object key) {}
-
     @Override
-    protected final boolean onRemovableRegister(Object key) {
-        IBinder binder = getBinderFromKey(key);
+    protected final void onRemovableListenerRegister() {
+        IBinder binder = getBinderFromKey(getKey());
         try {
             binder.linkToDeath(this, 0);
-            if (!onBinderRegister(key)) {
-                binder.unlinkToDeath(this, 0);
-                return false;
-            }
-            return true;
         } catch (RemoteException e) {
-            return false;
+            remove();
         }
+
+        onBinderListenerRegister();
     }
 
     @Override
-    protected final void onRemovableUnregister(Object key) {
-        IBinder binder = getBinderFromKey(key);
-        onBinderUnregister(key);
-        binder.unlinkToDeath(this, 0);
+    protected final void onRemovableListenerUnregister() {
+        onBinderListenerUnregister();
+        getBinderFromKey(getKey()).unlinkToDeath(this, 0);
     }
 
+    /**
+     * May be overridden in place of {@link #onRemovableListenerRegister()}.
+     */
+    protected void onBinderListenerRegister() {}
+
+    /**
+     * May be overridden in place of {@link #onRemovableListenerUnregister()}.
+     */
+    protected void onBinderListenerUnregister() {}
+
     @Override
     public void binderDied() {
         try {
@@ -98,7 +91,7 @@
         }
     }
 
-    private IBinder getBinderFromKey(Object key) {
+    private static IBinder getBinderFromKey(Object key) {
         if (key instanceof IBinder) {
             return (IBinder) key;
         } else if (key instanceof BinderKey) {
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f6ef859..f5889ce 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -25,8 +25,8 @@
 import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
 import com.android.internal.util.Preconditions;
-import com.android.server.location.listeners.ListenerRegistration.ListenerOperation;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -186,9 +186,8 @@
 
     /**
      * Adds a new registration with the given key. Registration may fail if
-     * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the
-     * registration will not be added. This method cannot be called to add a registration
-     * re-entrantly.
+     * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration
+     * will not be added. This method cannot be called to add a registration re-entrantly.
      */
     protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
         Objects.requireNonNull(key);
@@ -203,22 +202,12 @@
             // callbacks. further, we buffer service updates since adding a registration may
             // involve removing a prior registration. note that try-with-resources ordering is
             // meaningful here as well. we want to close the reentrancy guard first, as this may
-            // generate addition service updates, then close the update service buffer.
+            // generate additional service updates, then close the update service buffer.
             long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
-                if (mRegistrations.isEmpty()) {
-                    onRegister();
-                }
-
-                if (!registration.onRegister(key)) {
-                    if (mRegistrations.isEmpty()) {
-                        onUnregister();
-                    }
-
-                    return;
-                }
+                boolean wasEmpty = mRegistrations.isEmpty();
 
                 int index = mRegistrations.indexOfKey(key);
                 if (index >= 0) {
@@ -228,6 +217,11 @@
                     mRegistrations.put(key, registration);
                 }
 
+
+                if (wasEmpty) {
+                    onRegister();
+                }
+                registration.onRegister(key);
                 onRegistrationAdded(key, registration);
                 onRegistrationActiveChanged(registration);
             } finally {
@@ -270,7 +264,7 @@
             // callbacks. further, we buffer service updates since chains of removeLater()
             // invocations could result in multiple service updates. note that try-with-resources
             // ordering is meaningful here as well. we want to close the reentrancy guard first, as
-            // this may generate addition service updates, then close the update service buffer.
+            // this may generate additional service updates, then close the update service buffer.
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -327,8 +321,8 @@
         // callbacks themselves do not re-enter, as this could lead to out-of-order callbacks.
         // further, we buffer service updates since chains of removeLater() invocations could result
         // in multiple service updates. note that try-with-resources ordering is meaningful here as
-        // well. we want to close the reentrancy guard first, as this may generate addition service
-        // updates, then close the update service buffer.
+        // well. we want to close the reentrancy guard first, as this may generate additional
+        // service updates, then close the update service buffer.
         long identity = Binder.clearCallingIdentity();
         try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
              ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
@@ -393,6 +387,28 @@
     }
 
     /**
+     * Evaluates the given predicate for all registrations, and forces an {@link #updateService()}
+     * if any predicate returns true for an active registration. The predicate will always be
+     * evaluated for all registrations, even inactive registrations, or if it has already returned
+     * true for a prior registration.
+     */
+    protected final void updateService(Predicate<TRegistration> predicate) {
+        synchronized (mRegistrations) {
+            boolean updateService = false;
+            for (int i = 0; i < mRegistrations.size(); i++) {
+                TRegistration registration = mRegistrations.valueAt(i);
+                if (predicate.test(registration) && registration.isActive()) {
+                    updateService = true;
+                }
+            }
+
+            if (updateService) {
+                updateService();
+            }
+        }
+    }
+
+    /**
      * Begins buffering calls to {@link #updateService()} until {@link UpdateServiceLock#close()}
      * is called. This is useful to prevent extra work when combining multiple calls (for example,
      * buffering {@code updateService()} until after multiple adds/removes/updates occur.
@@ -412,7 +428,7 @@
             // since updating a registration can invoke a variety of callbacks, we need to ensure
             // those callbacks themselves do not re-enter, as this could lead to out-of-order
             // callbacks. note that try-with-resources ordering is meaningful here as well. we want
-            // to close the reentrancy guard first, as this may generate addition service updates,
+            // to close the reentrancy guard first, as this may generate additional service updates,
             // then close the update service buffer.
             long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
@@ -441,7 +457,7 @@
             // since updating a registration can invoke a variety of callbacks, we need to ensure
             // those callbacks themselves do not re-enter, as this could lead to out-of-order
             // callbacks. note that try-with-resources ordering is meaningful here as well. we want
-            // to close the reentrancy guard first, as this may generate addition service updates,
+            // to close the reentrancy guard first, as this may generate additional service updates,
             // then close the update service buffer.
             long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
@@ -511,8 +527,8 @@
     }
 
     /**
-     * Executes the given delivery operation for all active listeners. This is a convenience
-     * function equivalent to:
+     * Executes the given operation for all active listeners. This is a convenience function
+     * equivalent to:
      * <pre>
      * deliverToListeners(registration -> operation);
      * </pre>
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index 2a77c81..0bdd131 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -24,6 +24,7 @@
 import android.location.util.identity.CallerIdentity;
 import android.os.Process;
 
+import com.android.internal.listeners.ListenerExecutor;
 import com.android.server.FgThread;
 
 import java.util.Objects;
@@ -36,33 +37,21 @@
  * @param <TRequest>  request type
  * @param <TListener> listener type
  */
-public class ListenerRegistration<TRequest, TListener> {
-
-    /**
-     * An listener operation to perform.
-     *
-     * @param <TListener> listener type
-     */
-    public interface ListenerOperation<TListener> {
-        /**
-         * Performs the operation on the given listener
-         */
-        void operate(TListener listener) throws Exception;
-    }
+public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
 
     private final Executor mExecutor;
     private final @Nullable TRequest mRequest;
-    private final CallerIdentity mCallerIdentity;
+    private final CallerIdentity mIdentity;
 
     private boolean mActive;
 
     private volatile @Nullable TListener mListener;
 
-    protected ListenerRegistration(@Nullable TRequest request, CallerIdentity callerIdentity,
+    protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
             TListener listener) {
         // if a client is in the same process as us, binder calls will execute synchronously and
         // we shouldn't run callbacks directly since they might be run under lock and deadlock
-        if (callerIdentity.getPid() == Process.myPid()) {
+        if (identity.getPid() == Process.myPid()) {
             // there's a slight loophole here for pending intents - pending intent callbacks can
             // always be run on the direct executor since they're always asynchronous, but honestly
             // you shouldn't be using pending intent callbacks within the same process anyways
@@ -72,11 +61,15 @@
         }
 
         mRequest = request;
-        mCallerIdentity = Objects.requireNonNull(callerIdentity);
+        mIdentity = Objects.requireNonNull(identity);
         mActive = false;
         mListener = Objects.requireNonNull(listener);
     }
 
+    protected final Executor getExecutor() {
+        return mExecutor;
+    }
+
     /**
      * Returns the request associated with this listener, or null if one wasn't supplied.
      */
@@ -88,17 +81,13 @@
      * Returns the listener identity.
      */
     public final CallerIdentity getIdentity() {
-        return mCallerIdentity;
+        return mIdentity;
     }
 
     /**
-     * May be overridden by subclasses. Invoked when registration occurs. If this returns true,
-     * then registration will complete successfully. If this returns false, registration will fail,
-     * and {@link #onUnregister()} will not be called.
+     * May be overridden by subclasses. Invoked when registration occurs.
      */
-    protected boolean onRegister(Object key) {
-        return true;
-    }
+    protected void onRegister(Object key) {}
 
     /**
      * May be overridden by subclasses. Invoked when unregistration occurs.
@@ -137,35 +126,19 @@
 
     final void unregisterInternal() {
         mListener = null;
-    }
-
-    final void executeInternal(@NonNull ListenerOperation<TListener> operation) {
-        Objects.requireNonNull(operation);
-        mExecutor.execute(() -> {
-            TListener listener = mListener;
-            if (listener == null) {
-                return;
-            }
-
-            try {
-                operation.operate(listener);
-            } catch (Exception e) {
-                onOperationFailure(operation, e);
-            }
-        });
+        onListenerUnregister();
     }
 
     /**
-     * Invoked when an operation throws an exception, and run on the same executor as the operation.
+     * May be overridden by subclasses, however should rarely be needed. Invoked when the listener
+     * associated with this registration is unregistered, which may occur before the registration
+     * itself is unregistered. This immediately prevents the listener from being further invoked
+     * even if the various bookkeeping associated with unregistration has not occurred yet.
      */
-    protected void onOperationFailure(@NonNull ListenerOperation<TListener> operation,
-            @NonNull Exception exception) {
-        if (exception instanceof RuntimeException) {
-            throw (RuntimeException) exception;
-        } else {
-            // listeners should not throw exceptions that their registrations cannot handle
-            throw new UnsupportedOperationException(exception);
-        }
+    protected void onListenerUnregister() {};
+
+    final void executeInternal(@NonNull ListenerOperation<TListener> operation) {
+        executeSafely(mExecutor, () -> mListener, operation);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index 5339151..b5d2ef6 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -47,37 +47,28 @@
         super(tag, request, callerIdentity, listener);
     }
 
-    /**
-     * May be overridden in place of {@link #onRegister(Object)}. Should return true if registration
-     * is successful, and false otherwise.
-     */
-    protected boolean onPendingIntentRegister(Object key) {
-        return true;
-    }
-
-    /**
-     * May be overridden in place of {@link #onUnregister()}.
-     */
-    protected void onPendingIntentUnregister(Object key) {}
-
     @Override
-    protected final boolean onRemovableRegister(Object key) {
-        PendingIntent pendingIntent = getPendingIntentFromKey(key);
-        pendingIntent.registerCancelListener(this);
-        if (!onPendingIntentRegister(key)) {
-            pendingIntent.unregisterCancelListener(this);
-            return false;
-        }
-        return true;
+    protected final void onRemovableListenerRegister() {
+        getPendingIntentFromKey(getKey()).registerCancelListener(this);
+        onPendingIntentListenerRegister();
     }
 
     @Override
-    protected final void onRemovableUnregister(Object key) {
-        PendingIntent pendingIntent = getPendingIntentFromKey(key);
-        onPendingIntentUnregister(key);
-        pendingIntent.unregisterCancelListener(this);
+    protected final void onRemovableListenerUnregister() {
+        onPendingIntentListenerUnregister();
+        getPendingIntentFromKey(getKey()).unregisterCancelListener(this);
     }
 
+    /**
+     * May be overridden in place of {@link #onRemovableListenerRegister()}.
+     */
+    protected void onPendingIntentListenerRegister() {}
+
+    /**
+     * May be overridden in place of {@link #onRemovableListenerUnregister()}.
+     */
+    protected void onPendingIntentListenerUnregister() {}
+
     @Override
     public void onCancelled(PendingIntent intent) {
         if (Log.isLoggable(mTag, Log.DEBUG)) {
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index f40a617..e529a7d 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -47,7 +47,7 @@
      * with. Often this is easiest to accomplish by defining registration subclasses as non-static
      * inner classes of the multiplexer they are to be used with.
      */
-    protected abstract ListenerMultiplexer<?, TRequest, TListener, ?, ?> getOwner();
+    protected abstract ListenerMultiplexer<?, ? super TRequest, ? super TListener, ?, ?> getOwner();
 
     /**
      * Returns the key associated with this registration. May not be invoked before
@@ -58,50 +58,41 @@
     }
 
     /**
-     * Removes this registration. May not be invoked before {@link #onRegister(Object)} or after
-     * {@link #onUnregister()}.
+     * Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
+     * after {@link #onUnregister()}. It is safe to invoke this from within either function.
      */
-    public final void remove() {
-        getOwner().removeRegistration(Objects.requireNonNull(mKey), this);
+    public void remove() {
+        Object key = mKey;
+        if (key != null) {
+            getOwner().removeRegistration(key, this);
+        }
     }
 
     @Override
-    protected void onOperationFailure(ListenerOperation<TListener> operation, Exception e) {
-        if (e instanceof RuntimeException) {
-            throw (RuntimeException) e;
-        } else {
-            Log.w(mTag,
-                    "registration " + getIdentity() + " removed due to unexpected exception",
-                    e);
-            remove();
-        }
+    public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
+        Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e);
+        remove();
+    }
+
+    @Override
+    protected final void onRegister(Object key) {
+        mKey = Objects.requireNonNull(key);
+        onRemovableListenerRegister();
+    }
+
+    @Override
+    protected final void onUnregister() {
+        onRemovableListenerUnregister();
+        mKey = null;
     }
 
     /**
      * May be overridden in place of {@link #onRegister(Object)}.
      */
-    protected boolean onRemovableRegister(Object key) {
-        return true;
-    }
+    protected void onRemovableListenerRegister() {}
 
     /**
      * May be overridden in place of {@link #onUnregister()}.
      */
-    protected void onRemovableUnregister(Object key) {}
-
-    @Override
-    protected final boolean onRegister(Object key) {
-        mKey = Objects.requireNonNull(key);
-        if (!onRemovableRegister(key)) {
-            mKey = null;
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    protected final void onUnregister() {
-        onRemovableUnregister(mKey);
-        mKey = null;
-    }
+    protected void onRemovableListenerUnregister() {}
 }
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index 243ad93..78af79b 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -54,11 +54,9 @@
                 AppOpsManager.OP_COARSE_LOCATION,
                 null,
                 AppOpsManager.WATCH_FOREGROUND_CHANGES,
-                new AppOpsManager.OnOpChangedInternalListener() {
-                    public void onOpChanged(int op, String packageName) {
-                        // invoked on ui thread, move to fg thread so ui thread isn't blocked
-                        FgThread.getHandler().post(() -> notifyAppOpChanged(packageName));
-                    }
+                (op, packageName) -> {
+                    // invoked on ui thread, move to fg thread so ui thread isn't blocked
+                    FgThread.getHandler().post(() -> notifyAppOpChanged(packageName));
                 });
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index eba6fe6..375804a 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -2099,7 +2099,7 @@
                 boolean asSystemService, KeyEvent keyEvent, boolean needWakeLock) {
             if (mCurrentFullUserRecord.getMediaButtonSessionLocked()
                     instanceof MediaSession2Record) {
-                // TODO(jaewan): Implement
+                // TODO(jaewan): Make MediaSession2 to receive media key event
                 return;
             }
             MediaSessionRecord session = null;
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index 402355a..b678c89 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -244,6 +244,10 @@
     private MediaSessionRecordImpl findMediaButtonSession(int uid) {
         MediaSessionRecordImpl mediaButtonSession = null;
         for (MediaSessionRecordImpl session : mSessions) {
+            if (session instanceof MediaSession2Record) {
+                // TODO(jaewan): Make MediaSession2 to receive media key event
+                continue;
+            }
             if (uid == session.getUid()) {
                 if (session.checkPlaybackActiveState(
                         mAudioPlayerStateMonitor.isPlaybackActive(session.getUid()))) {
diff --git a/services/core/java/com/android/server/notification/ShortcutHelper.java b/services/core/java/com/android/server/notification/ShortcutHelper.java
index 94b690a..9c3d6d3 100644
--- a/services/core/java/com/android/server/notification/ShortcutHelper.java
+++ b/services/core/java/com/android/server/notification/ShortcutHelper.java
@@ -102,9 +102,13 @@
             HashMap<String, String> shortcutBubbles = mActiveShortcutBubbles.get(packageName);
             ArrayList<String> bubbleKeysToRemove = new ArrayList<>();
             if (shortcutBubbles != null) {
+                // Copy to avoid a concurrent modification exception when we remove bubbles from
+                // shortcutBubbles.
+                final Set<String> shortcutIds = new HashSet<>(shortcutBubbles.keySet());
+
                 // If we can't find one of our bubbles in the shortcut list, that bubble needs
                 // to be removed.
-                for (String shortcutId : shortcutBubbles.keySet()) {
+                for (String shortcutId : shortcutIds) {
                     boolean foundShortcut = false;
                     for (int i = 0; i < shortcuts.size(); i++) {
                         if (shortcuts.get(i).getId().equals(shortcutId)) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 3810e14..951f462 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -350,6 +350,11 @@
     public abstract boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds);
 
     /**
+     * Inform apexd that the boot has completed.
+     */
+    public abstract void markBootCompleted();
+
+    /**
      * Dumps various state information to the provided {@link PrintWriter} object.
      *
      * @param pw the {@link PrintWriter} object to send information to.
@@ -880,6 +885,15 @@
             }
         }
 
+        @Override
+        public void markBootCompleted() {
+            try {
+                waitForApexService().markBootCompleted();
+            } catch (RemoteException re) {
+                Slog.e(TAG, "Unable to contact apexservice", re);
+            }
+        }
+
         /**
          * Dump information about the packages contained in a particular cache
          * @param packagesCache the cache to print information about.
@@ -1127,6 +1141,11 @@
         }
 
         @Override
+        public void markBootCompleted() {
+            // No-op
+        }
+
+        @Override
         void dump(PrintWriter pw, String packageName) {
             // No-op
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 843f0ae..07f7567 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -525,7 +525,9 @@
 
         if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
             params.installFlags |= PackageManager.INSTALL_FROM_ADB;
-
+            // adb installs can override the installingPackageName, but not the
+            // initiatingPackageName
+            installerPackageName = null;
         } else {
             if (callingUid != Process.SYSTEM_UID) {
                 // The supplied installerPackageName must always belong to the calling app.
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index a2ada46..5f48871 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -268,6 +268,9 @@
     /** Uid of the creator of this session. */
     private final int mOriginalInstallerUid;
 
+    /** Package name of the app that created the installation session. */
+    private final String mOriginalInstallerPackageName;
+
     /** Uid of the owner of the installer session */
     @GuardedBy("mLock")
     private int mInstallerUid;
@@ -557,6 +560,7 @@
         mOriginalInstallerUid = installerUid;
         mInstallerUid = installerUid;
         mInstallSource = Objects.requireNonNull(installSource);
+        mOriginalInstallerPackageName = mInstallSource.installerPackageName;
         this.params = params;
         this.createdMillis = createdMillis;
         this.updatedMillis = createdMillis;
@@ -606,14 +610,8 @@
             }
         }
 
-        if (isIncrementalInstallation()) {
-            if (!IncrementalManager.isAllowed()) {
-                throw new IllegalArgumentException("Incremental installation not allowed.");
-            }
-            if (!isIncrementalInstallationAllowed(mPackageName)) {
-                throw new IllegalArgumentException(
-                        "Incremental installation of this package is not allowed.");
-            }
+        if (isIncrementalInstallation() && !IncrementalManager.isAllowed()) {
+            throw new IllegalArgumentException("Incremental installation not allowed.");
         }
     }
 
@@ -1668,11 +1666,6 @@
                 throw new IllegalArgumentException("Package is not valid", e);
             }
 
-            if (!mPackageName.equals(mInstallSource.installerPackageName)) {
-                throw new SecurityException("Can only transfer sessions that update the original "
-                        + "installer");
-            }
-
             mInstallerUid = newOwnerAppInfo.uid;
             mInstallSource = InstallSource.create(packageName, null, packageName, null);
         }
@@ -1754,7 +1747,7 @@
                         failure.error, failure.getLocalizedMessage(), null);
                 return;
             }
-            mPm.installStage(installingChildSessions);
+            mPm.installStage(installingSession, installingChildSessions);
         } else {
             mPm.installStage(installingSession);
         }
@@ -1873,6 +1866,34 @@
             }
         };
 
+        // An observer through which PMS returns the result of verification
+        // TODO(samiul): We are temporarily assigning two observer to ActiveInstallSession. One for
+        // installation and one for verification. This will be fixed within next few CLs.
+        final IPackageInstallObserver2 sessionVerificationObserver;
+        if (!hasParentSessionId()) {
+            // Avoid attaching this observer to child session since they won't use it.
+            sessionVerificationObserver = new IPackageInstallObserver2.Stub() {
+                @Override
+                public void onUserActionRequired(Intent intent) {
+                    throw new IllegalStateException();
+                }
+
+                @Override
+                public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+                        Bundle extras) {
+                    if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+                        // TODO(samiul): In future, packages will not be installed immediately after
+                        // verification. Package verification will return control back to here,
+                        // and we will have call into PMS again to install package.
+                        //
+                        // For now, this is a no op.
+                    }
+                }
+            };
+        } else {
+            sessionVerificationObserver = null;
+        }
+
         final UserHandle user;
         if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
             user = UserHandle.ALL;
@@ -1882,7 +1903,8 @@
 
         mRelinquished = true;
         return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, localObserver,
-                sessionId, params, mInstallerUid, mInstallSource, user, mSigningDetails);
+                sessionVerificationObserver, sessionId, params, mInstallerUid, mInstallSource, user,
+                mSigningDetails);
     }
 
     private static void maybeRenameFile(File from, File to) throws PackageManagerException {
@@ -2154,6 +2176,21 @@
             }
         }
 
+        if (isIncrementalInstallation() && !isIncrementalInstallationAllowed(mPackageName)) {
+            throw new PackageManagerException(
+                    PackageManager.INSTALL_FAILED_SESSION_INVALID,
+                    "Incremental installation of this package is not allowed.");
+        }
+
+        if (mInstallerUid != mOriginalInstallerUid) {
+            // Session has been transferred, check package name.
+            if (TextUtils.isEmpty(mPackageName) || !mPackageName.equals(
+                    mOriginalInstallerPackageName)) {
+                throw new PackageManagerException(PackageManager.INSTALL_FAILED_PACKAGE_CHANGED,
+                        "Can only transfer sessions that update the original installer");
+            }
+        }
+
         if (params.mode == SessionParams.MODE_FULL_INSTALL) {
             // Full installs must include a base package
             if (!stagedSplits.contains(null)) {
@@ -3183,6 +3220,7 @@
 
         pw.printPair("userId", userId);
         pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
+        pw.printPair("mOriginalInstallerPackageName", mOriginalInstallerPackageName);
         pw.printPair("installerPackageName", mInstallSource.installerPackageName);
         pw.printPair("installInitiatingPackageName", mInstallSource.initiatingPackageName);
         pw.printPair("installOriginatingPackageName", mInstallSource.originatingPackageName);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3bc0b89..daf7f72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -51,7 +51,6 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION;
-import static android.content.pm.PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
@@ -59,6 +58,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PROCESS_NOT_DEFINED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_SESSION_INVALID;
 import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
 import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
 import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE;
@@ -11080,6 +11080,21 @@
             pkgSetting.forceQueryableOverride = true;
         }
 
+        // If this is part of a standard install, set the initiating package name, else rely on
+        // previous device state.
+        if (reconciledPkg.installArgs != null) {
+            InstallSource installSource = reconciledPkg.installArgs.installSource;
+            if (installSource.initiatingPackageName != null) {
+                final PackageSetting ips = mSettings.mPackages.get(
+                        installSource.initiatingPackageName);
+                if (ips != null) {
+                    installSource = installSource.setInitiatingPackageSignatures(
+                            ips.signatures);
+                }
+            }
+            pkgSetting.setInstallSource(installSource);
+        }
+
         // TODO(toddke): Consider a method specifically for modifying the Package object
         // post scan; or, moving this stuff out of the Package object since it has nothing
         // to do with the package on disk.
@@ -12806,11 +12821,11 @@
         mHandler.sendMessage(msg);
     }
 
-    void installStage(List<ActiveInstallSession> children)
+    void installStage(ActiveInstallSession parent, List<ActiveInstallSession> children)
             throws PackageManagerException {
         final Message msg = mHandler.obtainMessage(INIT_COPY);
         final MultiPackageInstallParams params =
-                new MultiPackageInstallParams(UserHandle.ALL, children);
+                new MultiPackageInstallParams(UserHandle.ALL, parent, children);
         params.setTraceMethod("installStageMultiPackage")
                 .setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
@@ -14302,17 +14317,6 @@
         }
     }
 
-    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
-        if (args.mMultiPackageInstallParams != null) {
-            args.mMultiPackageInstallParams.tryProcessInstallRequest(args, currentStatus);
-        } else {
-            PackageInstalledInfo res = createPackageInstalledInfo(currentStatus);
-            processInstallRequestsAsync(
-                    res.returnCode == PackageManager.INSTALL_SUCCEEDED,
-                    Collections.singletonList(new InstallRequest(args, res)));
-        }
-    }
-
     // Queue up an async operation since the package installation may take a little while.
     private void processInstallRequestsAsync(boolean success,
             List<InstallRequest> installRequests) {
@@ -14693,13 +14697,17 @@
      * committed together.
      */
     class MultiPackageInstallParams extends HandlerParams {
+        private final IPackageInstallObserver2 mVerificationObserver;
         @NonNull
         private final ArrayList<InstallParams> mChildParams;
+        // TODO(samiul): mCurrentState will relocated to a install-specific class in future
         @NonNull
         private final Map<InstallArgs, Integer> mCurrentState;
+        private final Map<InstallParams, Integer> mVerificationState;
 
         MultiPackageInstallParams(
                 @NonNull UserHandle user,
+                @NonNull ActiveInstallSession parent,
                 @NonNull List<ActiveInstallSession> activeInstallSessions)
                 throws PackageManagerException {
             super(user);
@@ -14713,6 +14721,8 @@
                 this.mChildParams.add(childParams);
             }
             this.mCurrentState = new ArrayMap<>(mChildParams.size());
+            this.mVerificationState = new ArrayMap<>(mChildParams.size());
+            mVerificationObserver = parent.getVerificationObserver();
         }
 
         @Override
@@ -14729,6 +14739,7 @@
             }
         }
 
+        // TODO(samiul): this method will relocated to a install-specific class in future
         void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
             mCurrentState.put(args, currentStatus);
             if (mCurrentState.size() != mChildParams.size()) {
@@ -14752,6 +14763,28 @@
                     completeStatus == PackageManager.INSTALL_SUCCEEDED,
                     installRequests);
         }
+
+        void trySendVerificationCompleteNotification(InstallParams child, int currentStatus) {
+            mVerificationState.put(child, currentStatus);
+            if (mVerificationState.size() != mChildParams.size()) {
+                return;
+            }
+            int completeStatus = PackageManager.INSTALL_SUCCEEDED;
+            for (Integer status : mVerificationState.values()) {
+                if (status == PackageManager.INSTALL_UNKNOWN) {
+                    return;
+                } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+                    completeStatus = status;
+                    break;
+                }
+            }
+            try {
+                mVerificationObserver.onPackageInstalled(null, completeStatus,
+                        "Package Verification Result", new Bundle());
+            } catch (RemoteException e) {
+                Slog.i(TAG, "Observer no longer exists.");
+            }
+        }
     }
 
     class InstallParams extends HandlerParams {
@@ -14759,7 +14792,8 @@
 
         final OriginInfo origin;
         final MoveInfo move;
-        final IPackageInstallObserver2 observer;
+        final IPackageInstallObserver2 mInstallObserver;
+        private final IPackageInstallObserver2 mVerificationObserver;
         int installFlags;
         @NonNull final InstallSource installSource;
         final String volumeUuid;
@@ -14781,7 +14815,7 @@
         final int mDataLoaderType;
         final int mSessionId;
 
-        InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
+        InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 installObserver,
                 int installFlags, InstallSource installSource, String volumeUuid,
                 VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
                 String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
@@ -14791,7 +14825,8 @@
             super(user);
             this.origin = origin;
             this.move = move;
-            this.observer = observer;
+            this.mInstallObserver = installObserver;
+            this.mVerificationObserver = null;
             this.installFlags = installFlags;
             this.installSource = Preconditions.checkNotNull(installSource);
             this.volumeUuid = volumeUuid;
@@ -14829,7 +14864,8 @@
                     activeInstallSession.getInstallSource().installerPackageName,
                     activeInstallSession.getInstallerUid(),
                     sessionParams.installReason);
-            observer = activeInstallSession.getObserver();
+            mInstallObserver = activeInstallSession.getInstallObserver();
+            mVerificationObserver = activeInstallSession.getVerificationObserver();
             installFlags = sessionParams.installFlags;
             installSource = activeInstallSession.getInstallSource();
             volumeUuid = sessionParams.volumeUuid;
@@ -15394,17 +15430,45 @@
 
             if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
                 try {
-                    observer.onPackageInstalled(null, mRet, "Dry run", new Bundle());
+                    mInstallObserver.onPackageInstalled(null, mRet, "Dry run", new Bundle());
                 } catch (RemoteException e) {
                     Slog.i(TAG, "Observer no longer exists.");
                 }
                 return;
             }
+            sendVerificationCompleteNotification();
+
+            // TODO(samiul): In future return once verification is complete
+            processPendingInstall();
+        }
+
+        private void processPendingInstall() {
             InstallArgs args = createInstallArgs(this);
             if (mRet == PackageManager.INSTALL_SUCCEEDED) {
                 mRet = args.copyApk();
             }
-            processPendingInstall(args, mRet);
+            if (mParentInstallParams != null) {
+                mParentInstallParams.tryProcessInstallRequest(args, mRet);
+            } else {
+                PackageInstalledInfo res = createPackageInstalledInfo(mRet);
+                processInstallRequestsAsync(
+                        res.returnCode == PackageManager.INSTALL_SUCCEEDED,
+                        Collections.singletonList(new InstallRequest(args, res)));
+
+            }
+        }
+
+        private void sendVerificationCompleteNotification() {
+            if (mParentInstallParams != null) {
+                mParentInstallParams.trySendVerificationCompleteNotification(this, mRet);
+            } else {
+                try {
+                    mVerificationObserver.onPackageInstalled(null, mRet,
+                            "Package Verification Result", new Bundle());
+                } catch (RemoteException e) {
+                    Slog.i(TAG, "Observer no longer exists.");
+                }
+            }
         }
     }
 
@@ -15447,7 +15511,6 @@
         final PackageParser.SigningDetails signingDetails;
         final int installReason;
         final boolean forceQueryableOverride;
-        @Nullable final MultiPackageInstallParams mMultiPackageInstallParams;
         final int mDataLoaderType;
 
         // The list of instruction sets supported by this app. This is currently
@@ -15462,8 +15525,7 @@
                 List<String> whitelistedRestrictedPermissions,
                 int autoRevokePermissionsMode,
                 String traceMethod, int traceCookie, SigningDetails signingDetails,
-                int installReason, boolean forceQueryableOverride,
-                MultiPackageInstallParams multiPackageInstallParams, int dataLoaderType) {
+                int installReason, boolean forceQueryableOverride, int dataLoaderType) {
             this.origin = origin;
             this.move = move;
             this.installFlags = installFlags;
@@ -15481,20 +15543,18 @@
             this.signingDetails = signingDetails;
             this.installReason = installReason;
             this.forceQueryableOverride = forceQueryableOverride;
-            this.mMultiPackageInstallParams = multiPackageInstallParams;
             this.mDataLoaderType = dataLoaderType;
         }
 
         /** New install */
         InstallArgs(InstallParams params) {
-            this(params.origin, params.move, params.observer, params.installFlags,
+            this(params.origin, params.move, params.mInstallObserver, params.installFlags,
                     params.installSource, params.volumeUuid,
                     params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
                     params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
                     params.autoRevokePermissionsMode,
                     params.traceMethod, params.traceCookie, params.signingDetails,
-                    params.installReason, params.forceQueryableOverride,
-                    params.mParentInstallParams, params.mDataLoaderType);
+                    params.installReason, params.forceQueryableOverride, params.mDataLoaderType);
         }
 
         abstract int copyApk();
@@ -15585,7 +15645,7 @@
             super(OriginInfo.fromNothing(), null, null, 0, InstallSource.EMPTY,
                     null, null, instructionSets, null, null, null, MODE_DEFAULT, null, 0,
                     PackageParser.SigningDetails.UNKNOWN,
-                    PackageManager.INSTALL_REASON_UNKNOWN, false, null /* parent */,
+                    PackageManager.INSTALL_REASON_UNKNOWN, false,
                     DataLoaderType.NONE);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
@@ -16051,16 +16111,7 @@
                     ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
                 }
 
-                if (installSource.initiatingPackageName != null) {
-                    final PackageSetting ips = mSettings.mPackages.get(
-                            installSource.initiatingPackageName);
-                    if (ips != null) {
-                        installSource = installSource.setInitiatingPackageSignatures(
-                                ips.signatures);
-                    }
-                }
-                ps.setInstallSource(installSource);
-                mSettings.addInstallerPackageNames(installSource);
+                mSettings.addInstallerPackageNames(ps.installSource);
 
                 // When replacing an existing package, preserve the original install reason for all
                 // users that had the package installed before. Similarly for uninstall reasons.
@@ -17129,7 +17180,7 @@
         // Sanity check
         if (instantApp && onExternal) {
             Slog.i(TAG, "Incompatible ephemeral install; external=" + onExternal);
-            throw new PrepareFailure(PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+            throw new PrepareFailure(PackageManager.INSTALL_FAILED_SESSION_INVALID);
         }
 
         // Retrieve PackageSettings and parse package
@@ -17154,13 +17205,13 @@
             if (parsedPackage.getTargetSdkVersion() < Build.VERSION_CODES.O) {
                 Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
                                 + " does not target at least O");
-                throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+                throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
                         "Instant app package must target at least O");
             }
             if (parsedPackage.getSharedUserId() != null) {
                 Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
                         + " may not declare sharedUserId.");
-                throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+                throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
                         "Instant app package may not declare a sharedUserId");
             }
         }
@@ -17200,7 +17251,7 @@
                 < SignatureSchemeVersion.SIGNING_BLOCK_V2) {
             Slog.w(TAG, "Instant app package " + parsedPackage.getPackageName()
                     + " is not signed with at least APK Signature Scheme v2");
-            throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+            throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
                     "Instant app package must be signed with APK Signature Scheme v2 or greater");
         }
 
@@ -17406,7 +17457,7 @@
                         "Cannot install updates to system apps on sdcard");
             } else if (instantApp) {
                 // Abort update; system app can't be replaced with an instant app
-                throw new PrepareFailure(INSTALL_FAILED_INSTANT_APP_INVALID,
+                throw new PrepareFailure(INSTALL_FAILED_SESSION_INVALID,
                         "Cannot update a system app with an instant app");
             }
         }
@@ -17598,7 +17649,7 @@
                                             "Can't replace full app with instant app: " + pkgName11
                                                     + " for user: " + currentUser);
                                     throw new PrepareFailure(
-                                            PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+                                            PackageManager.INSTALL_FAILED_SESSION_INVALID);
                                 }
                             }
                         } else if (!ps.getInstantApp(args.user.getIdentifier())) {
@@ -17606,7 +17657,7 @@
                             Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
                                     + " for user: " + args.user.getIdentifier());
                             throw new PrepareFailure(
-                                    PackageManager.INSTALL_FAILED_INSTANT_APP_INVALID);
+                                    PackageManager.INSTALL_FAILED_SESSION_INVALID);
                         }
                     }
                 }
@@ -19123,9 +19174,7 @@
         final boolean systemApp = isSystemApp(ps);
 
         final int userId = user == null ? UserHandle.USER_ALL : user.getIdentifier();
-        if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS, userId)) {
-            unsuspendForSuspendingPackage(packageName, userId);
-        }
+
         if ((!systemApp || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)
                 && userId != UserHandle.USER_ALL) {
             // The caller is asking that the package only be deleted for a single
@@ -19183,6 +19232,20 @@
                     outInfo, writeSettings);
         }
 
+        // If the package removed had SUSPEND_APPS, unset any restrictions that might have been in
+        // place for all affected users.
+        int[] affectedUserIds = (outInfo != null) ? outInfo.removedUsers : null;
+        if (affectedUserIds == null) {
+            affectedUserIds = resolveUserIds(userId);
+        }
+        for (final int affectedUserId : affectedUserIds) {
+            if (ps.getPermissionsState().hasPermission(Manifest.permission.SUSPEND_APPS,
+                    affectedUserId)) {
+                unsuspendForSuspendingPackage(packageName, affectedUserId);
+                removeAllDistractingPackageRestrictions(affectedUserId);
+            }
+        }
+
         // Take a note whether we deleted the package for all users
         if (outInfo != null) {
             outInfo.removedForAllUsers = mPackages.get(ps.name) == null;
@@ -25508,7 +25571,10 @@
     static class ActiveInstallSession {
         private final String mPackageName;
         private final File mStagedDir;
-        private final IPackageInstallObserver2 mObserver;
+        private final IPackageInstallObserver2 mInstallObserver;
+        // TODO(samiul): We are temporarily assigning two observer to ActiveInstallSession. One for
+        // installation and one for verification. This will be fixed within next few CLs.
+        private final IPackageInstallObserver2 mVerificationObserver;
         private final int mSessionId;
         private final PackageInstaller.SessionParams mSessionParams;
         private final int mInstallerUid;
@@ -25516,12 +25582,15 @@
         private final UserHandle mUser;
         private final SigningDetails mSigningDetails;
 
-        ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
+        ActiveInstallSession(String packageName, File stagedDir,
+                IPackageInstallObserver2 installObserver,
+                IPackageInstallObserver2 verificationObserver,
                 int sessionId, PackageInstaller.SessionParams sessionParams, int installerUid,
                 InstallSource installSource, UserHandle user, SigningDetails signingDetails) {
             mPackageName = packageName;
             mStagedDir = stagedDir;
-            mObserver = observer;
+            mInstallObserver = installObserver;
+            mVerificationObserver = verificationObserver;
             mSessionId = sessionId;
             mSessionParams = sessionParams;
             mInstallerUid = installerUid;
@@ -25538,8 +25607,12 @@
             return mStagedDir;
         }
 
-        public IPackageInstallObserver2 getObserver() {
-            return mObserver;
+        public IPackageInstallObserver2 getInstallObserver() {
+            return mInstallObserver;
+        }
+
+        public IPackageInstallObserver2 getVerificationObserver() {
+            return mVerificationObserver;
         }
 
         public int getSessionId() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index d2afc9f..fd73d68 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -124,7 +124,6 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -297,7 +296,8 @@
                 case "get-stagedsessions":
                     return runListStagedSessions();
                 case "uninstall-system-updates":
-                    return uninstallSystemUpdates();
+                    String packageName = getNextArg();
+                    return uninstallSystemUpdates(packageName);
                 case "rollback-app":
                     return runRollbackApp();
                 case "get-moduleinfo":
@@ -415,15 +415,22 @@
         }
     }
 
-    private int uninstallSystemUpdates() {
+    private int uninstallSystemUpdates(String packageName) {
         final PrintWriter pw = getOutPrintWriter();
-        List<String> failedUninstalls = new LinkedList<>();
+        boolean failedUninstalls = false;
         try {
-            final ParceledListSlice<ApplicationInfo> packages =
-                    mInterface.getInstalledApplications(
-                            PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
             final IPackageInstaller installer = mInterface.getPackageInstaller();
-            List<ApplicationInfo> list = packages.getList();
+            final List<ApplicationInfo> list;
+            if (packageName == null) {
+                final ParceledListSlice<ApplicationInfo> packages =
+                        mInterface.getInstalledApplications(
+                                PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+                list = packages.getList();
+            } else {
+                list = new ArrayList<>(1);
+                list.add(mInterface.getApplicationInfo(packageName,
+                        PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM));
+            }
             for (ApplicationInfo info : list) {
                 if (info.isUpdatedSystemApp()) {
                     pw.println("Uninstalling updates to " + info.packageName + "...");
@@ -436,7 +443,8 @@
                     final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                             PackageInstaller.STATUS_FAILURE);
                     if (status != PackageInstaller.STATUS_SUCCESS) {
-                        failedUninstalls.add(info.packageName);
+                        failedUninstalls = true;
+                        pw.println("Couldn't uninstall package: " + info.packageName);
                     }
                 }
             }
@@ -446,10 +454,7 @@
                     + e.getMessage() + "]");
             return 0;
         }
-        if (!failedUninstalls.isEmpty()) {
-            pw.println("Failure [Couldn't uninstall packages: "
-                    + TextUtils.join(", ", failedUninstalls)
-                    + "]");
+        if (failedUninstalls) {
             return 0;
         }
         pw.println("Success");
@@ -3824,9 +3829,10 @@
         pw.println("  get-harmful-app-warning [--user <USER_ID>] <PACKAGE>");
         pw.println("    Return the harmful app warning message for the given app, if present");
         pw.println();
-        pw.println("  uninstall-system-updates");
-        pw.println("    Remove updates to all system applications and fall back to their /system " +
-                "version.");
+        pw.println("  uninstall-system-updates [<PACKAGE>]");
+        pw.println("    Removes updates to the given system application and falls back to its");
+        pw.println("    /system version. Does nothing if the given package is not a system app.");
+        pw.println("    If no package is specified, removes updates to all system applications.");
         pw.println("");
         pw.println("  get-moduleinfo [--all | --installed] [module-name]");
         pw.println("    Displays module info. If module-name is specified only that info is shown");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 21e15b9..53a3904 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -164,6 +164,7 @@
         public void onBootPhase(int phase) {
             if (phase == SystemService.PHASE_BOOT_COMPLETED && sStagingManager != null) {
                 sStagingManager.markStagedSessionsAsSuccessful();
+                sStagingManager.markBootCompleted();
             }
         }
     }
@@ -179,6 +180,10 @@
         }
     }
 
+    private void markBootCompleted() {
+        mApexManager.markBootCompleted();
+    }
+
     ParceledListSlice<PackageInstaller.SessionInfo> getSessions(int callingUid) {
         final List<PackageInstaller.SessionInfo> result = new ArrayList<>();
         synchronized (mStagedSessions) {
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index d4a71ed..fd5c6e9 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -38,6 +38,7 @@
 import android.util.Slog;
 
 import com.android.i18n.timezone.TimeZoneDataFiles;
+import com.android.i18n.timezone.TimeZoneFinder;
 import com.android.i18n.timezone.TzDataSetVersion;
 import com.android.i18n.timezone.ZoneInfoDb;
 import com.android.internal.annotations.VisibleForTesting;
@@ -49,7 +50,6 @@
 import com.android.timezone.distro.TimeZoneDistro;
 import com.android.timezone.distro.installer.TimeZoneDistroInstaller;
 
-import libcore.timezone.TimeZoneFinder;
 
 import java.io.File;
 import java.io.FileDescriptor;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a5e945b..2d50012 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -116,7 +116,6 @@
 import static com.android.server.wm.ActivityRecordProto.FILLS_PARENT;
 import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
 import static com.android.server.wm.ActivityRecordProto.FROZEN_BOUNDS;
-import static com.android.server.wm.ActivityRecordProto.IDENTIFIER;
 import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
 import static com.android.server.wm.ActivityRecordProto.IS_WAITING_FOR_TRANSITION_START;
 import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
@@ -268,6 +267,7 @@
 import android.service.dreams.DreamActivity;
 import android.service.dreams.DreamManagerInternal;
 import android.service.voice.IVoiceInteractionSession;
+import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Log;
@@ -2054,23 +2054,28 @@
     }
 
     static boolean canLaunchDreamActivity(String packageName) {
-        final DreamManagerInternal dreamManager =
-                LocalServices.getService(DreamManagerInternal.class);
-
-        // Verify that the package is the current active dream. The getActiveDreamComponent()
-        // call path does not acquire the DreamManager lock and thus is safe to use.
-        final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */);
-        if (activeDream == null || activeDream.getPackageName() == null
-                || !activeDream.getPackageName().equals(packageName)) {
+        if (packageName == null) {
             return false;
         }
 
-        // Verify that the device is dreaming.
         if (!LocalServices.getService(ActivityTaskManagerInternal.class).isDreaming()) {
             return false;
         }
 
-        return true;
+        final DreamManagerInternal dreamManager =
+                LocalServices.getService(DreamManagerInternal.class);
+
+        // Verify that the package is the current active dream or doze component. The
+        // getActiveDreamComponent() call path does not acquire the DreamManager lock and thus
+        // is safe to use.
+        final ComponentName activeDream = dreamManager.getActiveDreamComponent(false /* doze */);
+        final ComponentName activeDoze = dreamManager.getActiveDreamComponent(true /* doze */);
+        return TextUtils.equals(packageName, getPackageName(activeDream))
+                || TextUtils.equals(packageName, getPackageName(activeDoze));
+    }
+
+    private static String getPackageName(ComponentName componentName) {
+        return componentName != null ? componentName.getPackageName() : null;
     }
 
     private void setActivityType(boolean componentSpecified, int launchedFromUid, Intent intent,
@@ -2561,7 +2566,7 @@
             if (mayAdjustTop && ((ActivityStack) task).topRunningActivity(true /* focusableOnly */)
                     == null) {
                 task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
-                        shouldAdjustGlobalFocus);
+                            shouldAdjustGlobalFocus);
             }
 
             finishActivityResults(resultCode, resultData, resultGrants);
@@ -2581,7 +2586,7 @@
                 if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
                     Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
                 }
-                getDisplay().mDisplayContent.prepareAppTransition(transit, false);
+                mDisplayContent.prepareAppTransition(transit, false);
 
                 // When finishing the activity preemptively take the snapshot before the app window
                 // is marked as hidden and any configuration changes take place
@@ -2606,6 +2611,13 @@
 
                 if (endTask) {
                     mAtmService.getLockTaskController().clearLockedTask(task);
+                    // This activity was in the top focused stack and this is the last activity in
+                    // that task, give this activity a higher layer so it can stay on top before the
+                    // closing task transition be executed.
+                    if (mayAdjustTop) {
+                        mNeedsZBoost = true;
+                        mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
+                    }
                 }
             } else if (!isState(PAUSING)) {
                 if (mVisibleRequested) {
@@ -3052,6 +3064,8 @@
         // from the client it-self to the parent surface (owned by us).
         detachChildren();
 
+        clearAllDrawn();
+
         mPendingRelaunchCount++;
     }
 
@@ -6112,7 +6126,6 @@
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "AR#onAnimationFinished");
         mTransit = TRANSIT_UNSET;
         mTransitFlags = 0;
-        mNeedsZBoost = false;
         mNeedsAnimationBoundsLayer = false;
 
         setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
@@ -7605,7 +7618,6 @@
             bounds.dumpDebug(proto, FROZEN_BOUNDS);
         }
 
-        writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(STATE, mState.toString());
         proto.write(FRONT_OF_TASK, isRootOfTask());
         if (hasProcess()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 8bd6412..61c127c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2029,7 +2029,12 @@
                     if (r.mLaunchTaskBehind) {
                         transit = TRANSIT_TASK_OPEN_BEHIND;
                     } else if (getDisplay().isSingleTaskInstance()) {
+                        // If a new task is being launched in a single task display, we don't need
+                        // to play normal animation, but need to trigger a callback when an app
+                        // transition is actually handled. So ignore already prepared activity, and
+                        // override it.
                         transit = TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
+                        keepCurTransition = false;
                     } else {
                         // If a new task is being launched, then mark the existing top activity as
                         // supporting picture-in-picture while pausing only if the starting activity
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 947b75b..8666a9e 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1504,12 +1504,21 @@
     }
 
     void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
-        task.removeTaskActivitiesLocked(reason);
-        cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
-        mService.getLockTaskController().clearLockedTask(task);
-        mService.getTaskChangeNotificationController().notifyTaskStackChanged();
-        if (task.isPersistable) {
-            mService.notifyTaskPersisterLocked(null, true);
+        if (task.mInRemoveTask) {
+            // Prevent recursion.
+            return;
+        }
+        task.mInRemoveTask = true;
+        try {
+            task.performClearTask(reason);
+            cleanUpRemovedTaskLocked(task, killProcess, removeFromRecents);
+            mService.getLockTaskController().clearLockedTask(task);
+            mService.getTaskChangeNotificationController().notifyTaskStackChanged();
+            if (task.isPersistable) {
+                mService.notifyTaskPersisterLocked(null, true);
+            }
+        } finally {
+            task.mInRemoveTask = false;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 6f3ddc9..63e14bb 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -21,7 +21,6 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
-import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
 import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOW_TOKENS;
 
@@ -263,15 +262,6 @@
     }
 
     /**
-     * Root of the display area hierarchy.
-     */
-    public static class Root extends DisplayArea<DisplayArea> {
-        Root(WindowManagerService wms) {
-            super(wms, Type.ANY, "DisplayArea.Root", FEATURE_ROOT);
-        }
-    }
-
-    /**
      * DisplayArea that can be dimmed.
      */
     static class Dimmable extends DisplayArea<DisplayArea> {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index e4e9eec..709e4536 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -34,56 +34,24 @@
  */
 public abstract class DisplayAreaPolicy {
     protected final WindowManagerService mWmService;
-    protected final DisplayContent mContent;
 
     /**
      * The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
      */
-    protected final DisplayArea.Root mRoot;
-
-    /**
-     * The IME container. The IME's windows are automatically added to this container.
-     */
-    protected final DisplayArea<? extends WindowContainer> mImeContainer;
-
-    /**
-     * The task display areas. Tasks etc. are automatically added to these containers.
-     */
-    protected final List<TaskDisplayArea> mTaskDisplayAreas;
+    protected final RootDisplayArea mRoot;
 
     /**
      * Construct a new {@link DisplayAreaPolicy}
      *
      * @param wmService the window manager service instance
-     * @param content the display content for which the policy applies
      * @param root the root display area under which the policy operates
-     * @param imeContainer the ime container that the policy must attach
-     * @param taskDisplayAreas the task display areas that the policy must attach
-     *
-     * @see #attachDisplayAreas()
      */
-    protected DisplayAreaPolicy(WindowManagerService wmService,
-            DisplayContent content, DisplayArea.Root root,
-            DisplayArea<? extends WindowContainer> imeContainer,
-            List<TaskDisplayArea> taskDisplayAreas) {
+    protected DisplayAreaPolicy(WindowManagerService wmService, RootDisplayArea root) {
         mWmService = wmService;
-        mContent = content;
         mRoot = root;
-        mImeContainer = imeContainer;
-        mTaskDisplayAreas = taskDisplayAreas;
     }
 
     /**
-     * Called to ask the policy to set up the DisplayArea hierarchy. At a minimum this must:
-     *
-     * - attach mImeContainer to mRoot (or one of its descendants)
-     * - attach mTaskStacks to mRoot (or one of its descendants)
-     *
-     * Additionally, this is the right place to set up any other DisplayAreas as desired.
-     */
-    public abstract void attachDisplayAreas();
-
-    /**
      * Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
      *
      * This must attach the token to mRoot (or one of its descendants).
@@ -98,28 +66,28 @@
     /**
      * @return the number of task display areas on the display.
      */
-    public int getTaskDisplayAreaCount() {
-        return mTaskDisplayAreas.size();
-    }
+    public abstract int getTaskDisplayAreaCount();
 
     /**
      * @return the task display area at index.
      */
-    public TaskDisplayArea getTaskDisplayAreaAt(int index) {
-        return mTaskDisplayAreas.get(index);
-    }
+    public abstract TaskDisplayArea getTaskDisplayAreaAt(int index);
 
     /** Provider for platform-default display area policy. */
     static final class DefaultProvider implements DisplayAreaPolicy.Provider {
         @Override
         public DisplayAreaPolicy instantiate(WindowManagerService wmService,
-                DisplayContent content, DisplayArea.Root root,
+                DisplayContent content, RootDisplayArea root,
                 DisplayArea<? extends WindowContainer> imeContainer) {
             final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
                     "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
             final List<TaskDisplayArea> tdaList = new ArrayList<>();
             tdaList.add(defaultTaskDisplayArea);
-            return new DisplayAreaPolicyBuilder()
+
+            // Define the features that will be supported under the root of the whole logical
+            // display. The policy will build the DisplayArea hierarchy based on this.
+            DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+                    new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
                     .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy,
                             "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION)
                             .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
@@ -133,7 +101,12 @@
                             .all()
                             .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
                             .build())
-                    .build(wmService, content, root, imeContainer, tdaList);
+                    .setImeContainer(imeContainer)
+                    .setTaskDisplayAreas(tdaList);
+
+            // Instantiate the policy with the hierarchy defined above. This will create and attach
+            // all the necessary DisplayAreas to the root.
+            return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
         }
     }
 
@@ -146,16 +119,15 @@
      */
     public interface Provider {
         /**
-         * Instantiate a new DisplayAreaPolicy.
+         * Instantiates a new DisplayAreaPolicy. It should set up the {@link DisplayArea} hierarchy.
          *
          * @see DisplayAreaPolicy#DisplayAreaPolicy
          */
-        DisplayAreaPolicy instantiate(WindowManagerService wmService,
-                DisplayContent content, DisplayArea.Root root,
-                DisplayArea<? extends WindowContainer> imeContainer);
+        DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
+                RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer);
 
         /**
-         * Instantiate the device-specific {@link Provider}.
+         * Instantiates the device-specific {@link Provider}.
          */
         static Provider fromResources(Resources res) {
             String name = res.getString(
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
index 8a831d3..3de7b60 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
@@ -24,15 +24,19 @@
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
 
+import android.annotation.Nullable;
+import android.util.ArrayMap;
+
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * A builder for instantiating a complex {@link DisplayAreaPolicy}
@@ -51,7 +55,7 @@
  *             .build(...)
  *
  *     // Builds a policy with the following hierarchy:
- *      - DisplayArea.Root
+ *      - RootDisplayArea
  *        - Magnification
  *          - DisplayArea.Tokens (Wallpapers are attached here)
  *          - TaskDisplayArea
@@ -62,11 +66,200 @@
  *
  * </pre>
  *
- * // TODO(display-area): document more complex scenarios where we need multiple areas per feature.
+ * // TODO(b/157683117): document more complex scenarios where we need multiple areas per feature.
  */
 class DisplayAreaPolicyBuilder {
+    @Nullable private HierarchyBuilder mRootHierarchyBuilder;
 
-    private final ArrayList<Feature> mFeatures = new ArrayList<>();
+    /** Defines the root hierarchy. */
+    DisplayAreaPolicyBuilder setRootHierarchy(HierarchyBuilder rootHierarchyBuilder) {
+        // TODO(b/157683117): Add method to add sub root and root choosing func.
+        mRootHierarchyBuilder = rootHierarchyBuilder;
+        return this;
+    }
+
+    Result build(WindowManagerService wmService) {
+        Objects.requireNonNull(mRootHierarchyBuilder,
+                "Root must be set for the display area policy.");
+        Objects.requireNonNull(mRootHierarchyBuilder.mImeContainer, "Ime must not be null");
+        Preconditions.checkCollectionNotEmpty(mRootHierarchyBuilder.mTaskDisplayAreas,
+                "TaskDisplayAreas must not be empty");
+        mRootHierarchyBuilder.build();
+        return new Result(wmService, mRootHierarchyBuilder.mRoot);
+    }
+
+    /**
+     *  Builder to define {@link Feature} and {@link DisplayArea} hierarchy under a
+     * {@link RootDisplayArea}
+     */
+    static class HierarchyBuilder {
+        private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
+        private static final int LEAF_TYPE_IME_CONTAINERS = 2;
+        private static final int LEAF_TYPE_TOKENS = 0;
+
+        private final RootDisplayArea mRoot;
+        private final ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures = new ArrayList<>();
+        private final ArrayList<TaskDisplayArea> mTaskDisplayAreas = new ArrayList<>();
+        @Nullable
+        private DisplayArea<? extends WindowContainer> mImeContainer;
+
+        HierarchyBuilder(RootDisplayArea root) {
+            mRoot = root;
+        }
+
+        /** Adds {@link Feature} that applies to layers under this container. */
+        HierarchyBuilder addFeature(DisplayAreaPolicyBuilder.Feature feature) {
+            mFeatures.add(feature);
+            return this;
+        }
+
+        /**
+         * Sets {@link TaskDisplayArea} that are children of this hierarchy root.
+         * {@link DisplayArea} group must have at least one {@link TaskDisplayArea}.
+         */
+        HierarchyBuilder setTaskDisplayAreas(List<TaskDisplayArea> taskDisplayAreas) {
+            mTaskDisplayAreas.clear();
+            mTaskDisplayAreas.addAll(taskDisplayAreas);
+            return this;
+        }
+
+        /** Sets IME container as a child of this hierarchy root. */
+        HierarchyBuilder setImeContainer(DisplayArea<? extends WindowContainer> imeContainer) {
+            mImeContainer = imeContainer;
+            return this;
+        }
+
+        /** Builds the {@link DisplayArea} hierarchy below root. */
+        private void build() {
+            final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
+            final int maxWindowLayerCount = policy.getMaxWindowLayer();
+            final DisplayArea.Tokens[] displayAreaForLayer =
+                    new DisplayArea.Tokens[maxWindowLayerCount];
+            final Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureAreas =
+                    new ArrayMap<>(mFeatures.size());
+            for (int i = 0; i < mFeatures.size(); i++) {
+                featureAreas.put(mFeatures.get(i), new ArrayList<>());
+            }
+
+            // This method constructs the layer hierarchy with the following properties:
+            // (1) Every feature maps to a set of DisplayAreas
+            // (2) After adding a window, for every feature the window's type belongs to,
+            //     it is a descendant of one of the corresponding DisplayAreas of the feature.
+            // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
+            //     within a DisplayArea:
+            //      for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
+            //      max(z-range(a)) <= min(z-range(b))
+            //
+            // The algorithm below iteratively creates such a hierarchy:
+            //  - Initially, all windows are attached to the root.
+            //  - For each feature we create a set of DisplayAreas, by looping over the layers
+            //    - if the feature does apply to the current layer, we need to find a DisplayArea
+            //      for it to satisfy (2)
+            //      - we can re-use the previous layer's area if:
+            //         the current feature also applies to the previous layer, (to satisfy (3))
+            //         and the last feature that applied to the previous layer is the same as
+            //           the last feature that applied to the current layer (to satisfy (2))
+            //      - otherwise we create a new DisplayArea below the last feature that applied
+            //        to the current layer
+
+            PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
+            final PendingArea root = new PendingArea(null, 0, null);
+            Arrays.fill(areaForLayer, root);
+
+            // Create DisplayAreas to cover all defined features.
+            final int size = mFeatures.size();
+            for (int i = 0; i < size; i++) {
+                // Traverse the features with the order they are defined, so that the early defined
+                // feature will be on the top in the hierarchy.
+                final Feature feature = mFeatures.get(i);
+                PendingArea featureArea = null;
+                for (int layer = 0; layer < maxWindowLayerCount; layer++) {
+                    if (feature.mWindowLayers[layer]) {
+                        // This feature will be applied to this window layer.
+                        //
+                        // We need to find a DisplayArea for it:
+                        // We can reuse the existing one if it was created for this feature for the
+                        // previous layer AND the last feature that applied to the previous layer is
+                        // the same as the feature that applied to the current layer (so they are ok
+                        // to share the same parent DisplayArea).
+                        if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
+                            // No suitable DisplayArea:
+                            // Create a new one under the previous area (as parent) for this layer.
+                            featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
+                            areaForLayer[layer].mChildren.add(featureArea);
+                        }
+                        areaForLayer[layer] = featureArea;
+                    } else {
+                        // This feature won't be applied to this window layer. If it needs to be
+                        // applied to the next layer, we will need to create a new DisplayArea for
+                        // that.
+                        featureArea = null;
+                    }
+                }
+            }
+
+            // Create Tokens as leaf for every layer.
+            PendingArea leafArea = null;
+            int leafType = LEAF_TYPE_TOKENS;
+            for (int layer = 0; layer < maxWindowLayerCount; layer++) {
+                int type = typeOfLayer(policy, layer);
+                // Check whether we can reuse the same Tokens with the previous layer. This happens
+                // if the previous layer is the same type as the current layer AND there is no
+                // feature that applies to only one of them.
+                if (leafArea == null || leafArea.mParent != areaForLayer[layer]
+                        || type != leafType) {
+                    // Create a new Tokens for this layer.
+                    leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
+                    areaForLayer[layer].mChildren.add(leafArea);
+                    leafType = type;
+                    if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
+                        // We use the passed in TaskDisplayAreas for task container type of layer.
+                        // Skip creating Tokens even if there is no TDA.
+                        addTaskDisplayAreasToLayer(areaForLayer[layer]);
+                        leafArea.mSkipTokens = true;
+                    } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
+                        // We use the passed in ImeContainer for ime container type of layer.
+                        // Skip creating Tokens even if there is no ime container.
+                        leafArea.mExisting = mImeContainer;
+                        leafArea.mSkipTokens = true;
+                    }
+                }
+                leafArea.mMaxLayer = layer;
+            }
+            root.computeMaxLayer();
+
+            // We built a tree of PendingAreas above with all the necessary info to represent the
+            // hierarchy, now create and attach real DisplayAreas to the root.
+            root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);
+
+            // Notify the root that we have finished attaching all the DisplayAreas. Cache all the
+            // feature related collections there for fast access.
+            mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas, mTaskDisplayAreas);
+        }
+
+        /** Adds all {@link TaskDisplayArea} to the specified layer */
+        private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea) {
+            final int count = mTaskDisplayAreas.size();
+            for (int i = 0; i < count; i++) {
+                PendingArea leafArea =
+                        new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
+                leafArea.mExisting = mTaskDisplayAreas.get(i);
+                leafArea.mMaxLayer = APPLICATION_LAYER;
+                parentPendingArea.mChildren.add(leafArea);
+            }
+        }
+
+        private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
+            if (layer == APPLICATION_LAYER) {
+                return LEAF_TYPE_TASK_CONTAINERS;
+            } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
+                    || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
+                return LEAF_TYPE_IME_CONTAINERS;
+            } else {
+                return LEAF_TYPE_TOKENS;
+            }
+        }
+    }
 
     /** Supplier interface to provide a new created {@link DisplayArea}. */
     interface NewDisplayAreaSupplier {
@@ -116,8 +309,8 @@
             private NewDisplayAreaSupplier mNewDisplayAreaSupplier = DisplayArea::new;
 
             /**
-             * Build a new feature that applies to a set of window types as specified by the builder
-             * methods.
+             * Builds a new feature that applies to a set of window types as specified by the
+             * builder methods.
              *
              * <p>The set of types is updated iteratively in the order of the method invocations.
              * For example, {@code all().except(TYPE_STATUS_BAR)} expresses that a feature should
@@ -208,104 +401,9 @@
     }
 
     static class Result extends DisplayAreaPolicy {
-        private static final int LEAF_TYPE_TASK_CONTAINERS = 1;
-        private static final int LEAF_TYPE_IME_CONTAINERS = 2;
-        private static final int LEAF_TYPE_TOKENS = 0;
 
-        private final int mMaxWindowLayer = mWmService.mPolicy.getMaxWindowLayer();
-
-        private final ArrayList<Feature> mFeatures;
-        private final Map<Feature, List<DisplayArea<? extends WindowContainer>>> mAreas;
-        private final DisplayArea.Tokens[] mAreaForLayer = new DisplayArea.Tokens[mMaxWindowLayer];
-
-        Result(WindowManagerService wmService, DisplayContent content, DisplayArea.Root root,
-                DisplayArea<? extends WindowContainer> imeContainer,
-                List<TaskDisplayArea> taskDisplayAreas, ArrayList<Feature> features) {
-            super(wmService, content, root, imeContainer, taskDisplayAreas);
-            mFeatures = features;
-            mAreas = new HashMap<>(features.size());
-            for (int i = 0; i < mFeatures.size(); i++) {
-                mAreas.put(mFeatures.get(i), new ArrayList<>());
-            }
-        }
-
-        @Override
-        public void attachDisplayAreas() {
-            // This method constructs the layer hierarchy with the following properties:
-            // (1) Every feature maps to a set of DisplayAreas
-            // (2) After adding a window, for every feature the window's type belongs to,
-            //     it is a descendant of one of the corresponding DisplayAreas of the feature.
-            // (3) Z-order is maintained, i.e. if z-range(area) denotes the set of layers of windows
-            //     within a DisplayArea:
-            //      for every pair of DisplayArea siblings (a,b), where a is below b, it holds that
-            //      max(z-range(a)) <= min(z-range(b))
-            //
-            // The algorithm below iteratively creates such a hierarchy:
-            //  - Initially, all windows are attached to the root.
-            //  - For each feature we create a set of DisplayAreas, by looping over the layers
-            //    - if the feature does apply to the current layer, we need to find a DisplayArea
-            //      for it to satisfy (2)
-            //      - we can re-use the previous layer's area if:
-            //         the current feature also applies to the previous layer, (to satisfy (3))
-            //         and the last feature that applied to the previous layer is the same as
-            //           the last feature that applied to the current layer (to satisfy (2))
-            //      - otherwise we create a new DisplayArea below the last feature that applied
-            //        to the current layer
-
-
-            PendingArea[] areaForLayer = new PendingArea[mMaxWindowLayer];
-            final PendingArea root = new PendingArea(null, 0, null);
-            Arrays.fill(areaForLayer, root);
-
-            final int size = mFeatures.size();
-            for (int i = 0; i < size; i++) {
-                PendingArea featureArea = null;
-                for (int layer = 0; layer < mMaxWindowLayer; layer++) {
-                    final Feature feature = mFeatures.get(i);
-                    if (feature.mWindowLayers[layer]) {
-                        if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
-                            // No suitable DisplayArea - create a new one under the previous area
-                            // for this layer.
-                            featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
-                            areaForLayer[layer].mChildren.add(featureArea);
-                        }
-                        areaForLayer[layer] = featureArea;
-                    } else {
-                        featureArea = null;
-                    }
-                }
-            }
-
-            PendingArea leafArea = null;
-            int leafType = LEAF_TYPE_TOKENS;
-            for (int layer = 0; layer < mMaxWindowLayer; layer++) {
-                int type = typeOfLayer(mWmService.mPolicy, layer);
-                if (leafArea == null || leafArea.mParent != areaForLayer[layer]
-                        || type != leafType) {
-                    leafArea = new PendingArea(null, layer, areaForLayer[layer]);
-                    areaForLayer[layer].mChildren.add(leafArea);
-                    leafType = type;
-                    if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
-                        addTaskDisplayAreasToLayer(areaForLayer[layer], layer);
-                    } else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
-                        leafArea.mExisting = mImeContainer;
-                    }
-                }
-                leafArea.mMaxLayer = layer;
-            }
-            root.computeMaxLayer();
-            root.instantiateChildren(mRoot, mAreaForLayer, 0, mAreas);
-        }
-
-        /** Adds all task display areas to the specified layer */
-        private void addTaskDisplayAreasToLayer(PendingArea parentPendingArea, int layer) {
-            final int count = mTaskDisplayAreas.size();
-            for (int i = 0; i < count; i++) {
-                PendingArea leafArea = new PendingArea(null, layer, parentPendingArea);
-                leafArea.mExisting = mTaskDisplayAreas.get(i);
-                leafArea.mMaxLayer = layer;
-                parentPendingArea.mChildren.add(leafArea);
-            }
+        Result(WindowManagerService wmService, RootDisplayArea root) {
+            super(wmService, root);
         }
 
         @Override
@@ -316,77 +414,63 @@
 
         @VisibleForTesting
         DisplayArea.Tokens findAreaForToken(WindowToken token) {
-            int windowLayerFromType = token.getWindowLayerFromType();
-            if (windowLayerFromType == APPLICATION_LAYER) {
-                throw new IllegalArgumentException(
-                        "There shouldn't be WindowToken on APPLICATION_LAYER");
-            } else if (token.mRoundedCornerOverlay) {
-                windowLayerFromType = mMaxWindowLayer - 1;
-            }
-            return mAreaForLayer[windowLayerFromType];
+            // TODO(b/157683117): Choose root/sub root from OEM provided func.
+            return mRoot.findAreaForToken(token);
         }
 
         @VisibleForTesting
-        ArrayList<Feature> getFeatures() {
-            return mFeatures;
+        List<Feature> getFeatures() {
+            // TODO(b/157683117): Also get feature from sub root.
+            return new ArrayList<>(mRoot.mFeatures);
         }
 
         @Override
         public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(int featureId) {
-            for (int i = 0; i < mFeatures.size(); i++) {
-                Feature feature = mFeatures.get(i);
-                if (feature.getId() == featureId) {
-                    return getDisplayAreas(feature);
+            // TODO(b/157683117): Also get display areas from sub root.
+            List<Feature> features = getFeatures();
+            for (int i = 0; i < features.size(); i++) {
+                Feature feature = features.get(i);
+                if (feature.mId == featureId) {
+                    return new ArrayList<>(mRoot.mFeatureToDisplayAreas.get(feature));
                 }
             }
             return new ArrayList<>();
         }
 
-        public List<DisplayArea<? extends WindowContainer>> getDisplayAreas(Feature feature) {
-            return mAreas.get(feature);
+        @Override
+        public int getTaskDisplayAreaCount() {
+            // TODO(b/157683117): Also add TDA from sub root.
+            return mRoot.mTaskDisplayAreas.size();
         }
 
-        private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
-            if (layer == APPLICATION_LAYER) {
-                return LEAF_TYPE_TASK_CONTAINERS;
-            } else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
-                    || layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
-                return LEAF_TYPE_IME_CONTAINERS;
-            } else {
-                return LEAF_TYPE_TOKENS;
-            }
+        @Override
+        public TaskDisplayArea getTaskDisplayAreaAt(int index) {
+            // TODO(b/157683117): Get TDA from root/sub root based on their z-order.
+            return mRoot.mTaskDisplayAreas.get(index);
         }
     }
 
-    DisplayAreaPolicyBuilder addFeature(Feature feature) {
-        mFeatures.add(feature);
-        return this;
-    }
-
-    protected List<Feature> getFeatures() {
-        return mFeatures;
-    }
-
-    Result build(WindowManagerService wmService,
-            DisplayContent content, DisplayArea.Root root,
-            DisplayArea<? extends WindowContainer> imeContainer,
-            List<TaskDisplayArea> taskDisplayAreas) {
-
-        return new Result(wmService, content, root, imeContainer, taskDisplayAreas, new ArrayList<>(
-                mFeatures));
-    }
-
     static class PendingArea {
         final int mMinLayer;
         final ArrayList<PendingArea> mChildren = new ArrayList<>();
         final Feature mFeature;
         final PendingArea mParent;
         int mMaxLayer;
-        DisplayArea mExisting;
 
-        PendingArea(Feature feature,
-                int minLayer,
-                PendingArea parent) {
+        /** If not {@code null}, use this instead of creating a {@link DisplayArea.Tokens}. */
+        @Nullable DisplayArea mExisting;
+
+        /**
+         * Whether to skip creating a {@link DisplayArea.Tokens} if {@link #mExisting} is
+         * {@code null}.
+         *
+         * This will be set for {@link HierarchyBuilder#LEAF_TYPE_IME_CONTAINERS} and
+         * {@link HierarchyBuilder#LEAF_TYPE_TASK_CONTAINERS}, because we don't want to create
+         * {@link DisplayArea.Tokens} for them even if they are not set.
+         */
+        boolean mSkipTokens = false;
+
+        PendingArea(Feature feature, int minLayer, PendingArea parent) {
             mMinLayer = minLayer;
             mFeature = feature;
             mParent = parent;
@@ -399,13 +483,17 @@
             return mMaxLayer;
         }
 
-        void instantiateChildren(DisplayArea<DisplayArea> parent,
-                DisplayArea.Tokens[] areaForLayer, int level, Map<Feature, List<DisplayArea<?
-                extends WindowContainer>>> areas) {
+        void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
+                int level, Map<Feature, List<DisplayArea<? extends WindowContainer>>> areas) {
             mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
             for (int i = 0; i < mChildren.size(); i++) {
                 final PendingArea child = mChildren.get(i);
                 final DisplayArea area = child.createArea(parent, areaForLayer);
+                if (area == null) {
+                    // TaskDisplayArea and ImeContainer can be set at different hierarchy, so it can
+                    // be null.
+                    continue;
+                }
                 parent.addChild(area, WindowContainer.POSITION_TOP);
                 if (child.mFeature != null) {
                     areas.get(child.mFeature).add(area);
@@ -414,11 +502,15 @@
             }
         }
 
+        @Nullable
         private DisplayArea createArea(DisplayArea<DisplayArea> parent,
                 DisplayArea.Tokens[] areaForLayer) {
             if (mExisting != null) {
                 return mExisting;
             }
+            if (mSkipTokens) {
+                return null;
+            }
             DisplayArea.Type type;
             if (mMinLayer > APPLICATION_LAYER) {
                 type = DisplayArea.Type.ABOVE_TASKS;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d21eb70..9c330ec 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -231,7 +231,7 @@
  * Utility class for keeping track of the WindowStates and other pertinent contents of a
  * particular Display.
  */
-class DisplayContent extends DisplayArea.Root implements WindowManagerPolicy.DisplayContentInfo {
+class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
 
@@ -990,7 +990,6 @@
         // Setup the policy and build the display area hierarchy.
         mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
                 mWmService, this /* content */, this /* root */, mImeWindowsContainers);
-        mDisplayAreaPolicy.attachDisplayAreas();
 
         // Sets the display content for the children.
         onDisplayChanged(this);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 44dc687..96d10a1 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -3299,16 +3299,6 @@
             }
         }
         final WindowState win = winCandidate;
-        if (win.getAttrs().type == TYPE_NOTIFICATION_SHADE && isKeyguardShowing()
-                && isKeyguardOccluded()) {
-            // We are updating at a point where the keyguard has gotten
-            // focus, but we were last in a state where the top window is
-            // hiding it.  This is probably because the keyguard as been
-            // shown while the top window was displayed, so we want to ignore
-            // it here because this is just a very transient change and it
-            // will quickly lose focus once it correctly gets hidden.
-            return 0;
-        }
 
         mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 8734b5e..3e88566 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -19,10 +19,13 @@
 import static android.os.Process.myPid;
 import static android.os.Process.myUid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
 import static android.view.WindowManager.INPUT_CONSUMER_PIP;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.INPUT_CONSUMER_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
@@ -477,12 +480,11 @@
                     mService.getRecentsAnimationController();
             final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
                     && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
-            if (inputChannel == null || inputWindowHandle == null || w.mRemoved
-                    || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer)) {
+            if (inputWindowHandle == null || w.mRemoved) {
                 if (w.mWinAnimator.hasSurface()) {
                     mInputTransaction.setInputWindowInfo(
-                        w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
-                        mInvalidInputWindow);
+                            w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+                            mInvalidInputWindow);
                 }
                 // Skip this window because it cannot possibly receive input.
                 return;
@@ -491,9 +493,23 @@
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
             final int type = w.mAttrs.type;
-            final boolean hasFocus = w.isFocused();
             final boolean isVisible = w.isVisibleLw();
 
+            // Assign an InputInfo with type to the overlay window which can't receive input event.
+            // This is used to omit Surfaces from occlusion detection.
+            if (inputChannel == null
+                    || (w.cantReceiveTouchInput() && !shouldApplyRecentsInputConsumer))  {
+                if (!w.mWinAnimator.hasSurface()) {
+                    return;
+                }
+                populateOverlayInputInfo(inputWindowHandle, w.getName(), type, isVisible);
+                mInputTransaction.setInputWindowInfo(
+                        w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
+                        inputWindowHandle);
+                return;
+            }
+
+            final boolean hasFocus = w.isFocused();
             if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                 if (recentsAnimationController.updateInputConsumerForApp(
                         mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
@@ -555,6 +571,28 @@
         }
     }
 
+    // This would reset InputWindowHandle fields to prevent it could be found by input event.
+    // We need to check if any new field of InputWindowHandle could impact the result.
+    private static void populateOverlayInputInfo(final InputWindowHandle inputWindowHandle,
+            final String name, final int type, final boolean isVisible) {
+        inputWindowHandle.name = name;
+        inputWindowHandle.layoutParamsType = type;
+        inputWindowHandle.dispatchingTimeoutNanos =
+                WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
+        inputWindowHandle.visible = isVisible;
+        inputWindowHandle.canReceiveKeys = false;
+        inputWindowHandle.hasFocus = false;
+        inputWindowHandle.ownerPid = myPid();
+        inputWindowHandle.ownerUid = myUid();
+        inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
+        inputWindowHandle.scaleFactor = 1;
+        inputWindowHandle.layoutParamsFlags =
+                FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE | FLAG_NOT_FOCUSABLE;
+        inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
+        inputWindowHandle.touchableRegion.setEmpty();
+        inputWindowHandle.setTouchableRegionCrop(null);
+    }
+
     /**
      * Helper function to generate an InputInfo with type SECURE_SYSTEM_OVERLAY. This input
      * info will not have an input channel or be touchable, but is used to omit Surfaces
@@ -564,16 +602,7 @@
     static void setTrustedOverlayInputInfo(SurfaceControl sc, SurfaceControl.Transaction t,
             int displayId, String name) {
         InputWindowHandle inputWindowHandle = new InputWindowHandle(null, displayId);
-        inputWindowHandle.name = name;
-        inputWindowHandle.layoutParamsType = TYPE_SECURE_SYSTEM_OVERLAY;
-        inputWindowHandle.dispatchingTimeoutNanos = -1;
-        inputWindowHandle.visible = true;
-        inputWindowHandle.canReceiveKeys = false;
-        inputWindowHandle.hasFocus = false;
-        inputWindowHandle.ownerPid = myPid();
-        inputWindowHandle.ownerUid = myUid();
-        inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
-        inputWindowHandle.scaleFactor = 1;
+        populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
         t.setInputWindowInfo(sc, inputWindowHandle);
     }
 }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 4c10d581..9c535e4 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -133,10 +133,11 @@
      * Update the Keyguard showing state.
      */
     void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) {
-        // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
-        final boolean keyguardChanged = keyguardShowing != mKeyguardShowing
-                || mKeyguardGoingAway && keyguardShowing;
         final boolean aodChanged = aodShowing != mAodShowing;
+        // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
+        // Do not reset keyguardChanged status if this is aodChanged.
+        final boolean keyguardChanged = (keyguardShowing != mKeyguardShowing)
+                || (mKeyguardGoingAway && keyguardShowing && !aodChanged);
         if (!keyguardChanged && !aodChanged) {
             return;
         }
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
new file mode 100644
index 0000000..9d40854
--- /dev/null
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
+
+import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root
+ * of the whole logical display, or the root of a {@link DisplayArea} group.
+ */
+class RootDisplayArea extends DisplayArea<DisplayArea> {
+
+    /** {@link Feature} that are supported in this {@link DisplayArea} hierarchy. */
+    List<DisplayAreaPolicyBuilder.Feature> mFeatures;
+
+    /**
+     * Mapping from policy supported {@link Feature} to list of {@link DisplayArea} created to cover
+     * all the window types that the {@link Feature} will be applied to.
+     */
+    Map<Feature, List<DisplayArea<? extends WindowContainer>>> mFeatureToDisplayAreas;
+
+    /** Mapping from window layer to {@link DisplayArea.Tokens} that holds windows on that layer. */
+    private DisplayArea.Tokens[] mAreaForLayer;
+
+    /**
+     * List of {@link TaskDisplayArea} that are attached to this {@link DisplayArea} hierarchy. The
+     * order is the same as their z-order.
+     *
+     * TODO(b/157683117): Instead of caching the TDAs, always traverse the hierarchy to get them.
+     */
+    ArrayList<TaskDisplayArea> mTaskDisplayAreas;
+
+    /** Whether the hierarchy has been built. */
+    private boolean mHasBuiltHierarchy;
+
+    RootDisplayArea(WindowManagerService wms) {
+        super(wms, Type.ANY, "RootDisplayArea", FEATURE_ROOT);
+    }
+
+    /** Finds the {@link DisplayArea.Tokens} that this type of window should be attached to. */
+    DisplayArea.Tokens findAreaForToken(WindowToken token) {
+        int windowLayerFromType = token.getWindowLayerFromType();
+        if (windowLayerFromType == APPLICATION_LAYER) {
+            throw new IllegalArgumentException(
+                    "There shouldn't be WindowToken on APPLICATION_LAYER");
+        } else if (token.mRoundedCornerOverlay) {
+            windowLayerFromType = mAreaForLayer.length - 1;
+        }
+        return mAreaForLayer[windowLayerFromType];
+    }
+
+    /** Callback after {@link DisplayArea} hierarchy has been built. */
+    void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer,
+            Map<Feature, List<DisplayArea<? extends WindowContainer>>> featureToDisplayAreas,
+            ArrayList<TaskDisplayArea> taskDisplayAreas) {
+        if (mHasBuiltHierarchy) {
+            throw new IllegalStateException("Root should only build the hierarchy once");
+        }
+        mHasBuiltHierarchy = true;
+        mFeatures = Collections.unmodifiableList(features);
+        mAreaForLayer = areaForLayer;
+        mTaskDisplayAreas = taskDisplayAreas;
+        mFeatureToDisplayAreas = featureToDisplayAreas;
+    }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1530757..83b7543 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -433,6 +433,13 @@
     static final int FLAG_FORCE_HIDDEN_FOR_TASK_ORG = 1 << 1;
     private int mForceHiddenFlags = 0;
 
+    // TODO(b/160201781): Revisit double invocation issue in Task#removeChild.
+    /**
+     * Skip {@link ActivityStackSupervisor#removeTask(Task, boolean, boolean, String)} execution if
+     * {@code true} to prevent double traversal of {@link #mChildren} in a loop.
+     */
+    boolean mInRemoveTask;
+
     // When non-null, this is a transaction that will get applied on the next frame returned after
     // a relayout is requested from the client. While this is only valid on a leaf task; since the
     // transaction can effect an ancestor task, this also needs to keep track of the ancestor task
@@ -1492,11 +1499,8 @@
         return autoRemoveRecents || (!hasChild() && !getHasBeenVisible());
     }
 
-    /**
-     * Completely remove all activities associated with an existing
-     * task starting at a specified index.
-     */
-    private void performClearTaskAtIndexLocked(String reason) {
+    /** Completely remove all activities associated with an existing task. */
+    void performClearTask(String reason) {
         // Broken down into to cases to avoid object create due to capturing mStack.
         if (getStack() == null) {
             forAllActivities((r) -> {
@@ -1520,7 +1524,7 @@
      */
     void performClearTaskLocked() {
         mReuseTask = true;
-        performClearTaskAtIndexLocked("clear-task-all");
+        performClearTask("clear-task-all");
         mReuseTask = false;
     }
 
@@ -1581,11 +1585,6 @@
         return false;
     }
 
-    void removeTaskActivitiesLocked(String reason) {
-        // Just remove the entire task.
-        performClearTaskAtIndexLocked(reason);
-    }
-
     String lockTaskAuthToString() {
         switch (mLockTaskAuth) {
             case LOCK_TASK_AUTH_DONT_LOCK: return "LOCK_TASK_AUTH_DONT_LOCK";
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 205a8d2..67673dc 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -52,6 +52,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.os.UserHandle;
+import android.util.IntArray;
 import android.util.Slog;
 import android.view.SurfaceControl;
 import android.window.WindowContainerTransaction;
@@ -106,6 +107,9 @@
     private final ArrayList<ActivityStack> mTmpAlwaysOnTopStacks = new ArrayList<>();
     private final ArrayList<ActivityStack> mTmpNormalStacks = new ArrayList<>();
     private final ArrayList<ActivityStack> mTmpHomeStacks = new ArrayList<>();
+    private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
+    private int mTmpLayerForSplitScreenDividerAnchor;
+    private int mTmpLayerForAnimationLayer;
 
     private ArrayList<Task> mTmpTasks = new ArrayList<>();
 
@@ -326,14 +330,6 @@
         final boolean moveToTop = position >= getChildCount() - 1;
         final boolean moveToBottom = (position == POSITION_BOTTOM || position == 0);
 
-        // Reset mPreferredTopFocusableStack before positioning to top or {@link
-        // ActivityStackSupervisor#updateTopResumedActivityIfNeeded()} won't update the top
-        // resumed activity.
-        final boolean wasContained = mChildren.contains(child);
-        if (moveToTop && wasContained && child.isFocusable()) {
-            mPreferredTopFocusableStack = null;
-        }
-
         if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
             // This stack is always-on-top, override the default behavior.
             Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
@@ -374,6 +370,9 @@
         } else if (mPreferredTopFocusableStack == child) {
             mPreferredTopFocusableStack = null;
         }
+
+        // Update the top resumed activity because the preferred top focusable task may be changed.
+        mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded();
     }
 
     /**
@@ -633,39 +632,72 @@
 
         int layer = 0;
         // Place home stacks to the bottom.
-        for (int i = 0; i < mTmpHomeStacks.size(); i++) {
-            mTmpHomeStacks.get(i).assignLayer(t, layer++);
-        }
+        layer = adjustRootTaskLayer(t, mTmpHomeStacks, layer, false /* normalStacks */);
         // The home animation layer is between the home stacks and the normal stacks.
         final int layerForHomeAnimationLayer = layer++;
-        int layerForSplitScreenDividerAnchor = layer++;
-        int layerForAnimationLayer = layer++;
-        for (int i = 0; i < mTmpNormalStacks.size(); i++) {
-            final ActivityStack s = mTmpNormalStacks.get(i);
-            s.assignLayer(t, layer++);
-            if (s.inSplitScreenWindowingMode()) {
-                // The split screen divider anchor is located above the split screen window.
-                layerForSplitScreenDividerAnchor = layer++;
-            }
-            if (s.isTaskAnimating() || s.isAppTransitioning()) {
-                // The animation layer is located above the highest animating stack and no
-                // higher.
-                layerForAnimationLayer = layer++;
-            }
-        }
+        mTmpLayerForSplitScreenDividerAnchor = layer++;
+        mTmpLayerForAnimationLayer = layer++;
+        layer = adjustRootTaskLayer(t, mTmpNormalStacks, layer, true /* normalStacks */);
+
         // The boosted animation layer is between the normal stacks and the always on top
         // stacks.
         final int layerForBoostedAnimationLayer = layer++;
-        for (int i = 0; i < mTmpAlwaysOnTopStacks.size(); i++) {
-            mTmpAlwaysOnTopStacks.get(i).assignLayer(t, layer++);
-        }
+        adjustRootTaskLayer(t, mTmpAlwaysOnTopStacks, layer, false /* normalStacks */);
 
         t.setLayer(mHomeAppAnimationLayer, layerForHomeAnimationLayer);
-        t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
-        t.setLayer(mSplitScreenDividerAnchor, layerForSplitScreenDividerAnchor);
+        t.setLayer(mAppAnimationLayer, mTmpLayerForAnimationLayer);
+        t.setLayer(mSplitScreenDividerAnchor, mTmpLayerForSplitScreenDividerAnchor);
         t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
     }
 
+    private int adjustNormalStackLayer(ActivityStack s, int layer) {
+        if (s.inSplitScreenWindowingMode()) {
+            // The split screen divider anchor is located above the split screen window.
+            mTmpLayerForSplitScreenDividerAnchor = layer++;
+        }
+        if (s.isTaskAnimating() || s.isAppTransitioning()) {
+            // The animation layer is located above the highest animating stack and no
+            // higher.
+            mTmpLayerForAnimationLayer = layer++;
+        }
+        return layer;
+    }
+
+    /**
+     * Adjusts the layer of the stack which belongs to the same group.
+     * Note that there are three stack groups: home stacks, always on top stacks, and normal stacks.
+     *
+     * @param startLayer The beginning layer of this group of stacks.
+     * @param normalStacks Set {@code true} if this group is neither home nor always on top.
+     * @return The adjusted layer value.
+     */
+    private int adjustRootTaskLayer(SurfaceControl.Transaction t, ArrayList<ActivityStack> stacks,
+            int startLayer, boolean normalStacks) {
+        mTmpNeedsZBoostIndexes.clear();
+        final int stackSize = stacks.size();
+        for (int i = 0; i < stackSize; i++) {
+            final ActivityStack stack = stacks.get(i);
+            if (!stack.needsZBoost()) {
+                stack.assignLayer(t, startLayer++);
+                if (normalStacks) {
+                    startLayer = adjustNormalStackLayer(stack, startLayer);
+                }
+            } else {
+                mTmpNeedsZBoostIndexes.add(i);
+            }
+        }
+
+        final int zBoostSize = mTmpNeedsZBoostIndexes.size();
+        for (int i = 0; i < zBoostSize; i++) {
+            final ActivityStack stack = stacks.get(mTmpNeedsZBoostIndexes.get(i));
+            stack.assignLayer(t, startLayer++);
+            if (normalStacks) {
+                startLayer = adjustNormalStackLayer(stack, startLayer);
+            }
+        }
+        return startLayer;
+    }
+
     @Override
     SurfaceControl getAppAnimationLayer(@AnimationLayer int animationLayer) {
         switch (animationLayer) {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 37c3afb..f13dfdd 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -40,6 +40,7 @@
 import android.util.Slog;
 import android.view.InsetsSource;
 import android.view.InsetsState;
+import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl;
 import android.view.ThreadedRenderer;
 import android.view.WindowInsets;
@@ -480,7 +481,9 @@
                 task.getTaskDescription().getBackgroundColor(), 255);
         final LayoutParams attrs = mainWindow.getAttrs();
         final InsetsPolicy insetsPolicy = mainWindow.getDisplayContent().getInsetsPolicy();
-        final InsetsState insetsState = insetsPolicy.getInsetsForDispatch(mainWindow);
+        final InsetsState insetsState =
+                new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow));
+        mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
         final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState);
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
@@ -600,6 +603,15 @@
         return 0;
     }
 
+    static void mergeInsetsSources(InsetsState base, InsetsState other) {
+        for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
+            final InsetsSource source = other.peekSource(type);
+            if (source != null) {
+                base.addSource(source);
+            }
+        }
+    }
+
     static Rect getSystemBarInsets(Rect frame, InsetsState state) {
         return state.calculateInsets(frame, null /* ignoringVisibilityState */,
                 false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index b07398f..f3c7a5d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -42,6 +42,7 @@
 import static com.android.internal.policy.DecorView.getNavigationBarRect;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets;
+import static com.android.server.wm.TaskSnapshotController.mergeInsetsSources;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -243,7 +244,9 @@
 
             final InsetsPolicy insetsPolicy = topFullscreenOpaqueWindow.getDisplayContent()
                     .getInsetsPolicy();
-            insetsState = insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow);
+            insetsState =
+                    new InsetsState(insetsPolicy.getInsetsForDispatch(topFullscreenOpaqueWindow));
+            mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState());
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5a73fab..e3d6530 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -43,6 +43,7 @@
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowContainerChildProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowContainerProto.CONFIGURATION_CONTAINER;
+import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
 import static com.android.server.wm.WindowContainerProto.ORIENTATION;
 import static com.android.server.wm.WindowContainerProto.SURFACE_ANIMATOR;
 import static com.android.server.wm.WindowContainerProto.VISIBLE;
@@ -1940,6 +1941,7 @@
         super.dumpDebug(proto, CONFIGURATION_CONTAINER, logLevel);
         proto.write(ORIENTATION, mOrientation);
         proto.write(VISIBLE, isVisible);
+        writeIdentifierToProto(proto, IDENTIFIER);
         if (mSurfaceAnimator.isAnimating()) {
             mSurfaceAnimator.dumpDebug(proto, SURFACE_ANIMATOR);
         }
@@ -2440,6 +2442,7 @@
     protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
         doAnimationFinished(type, anim);
         mWmService.onAnimationFinished();
+        mNeedsZBoost = false;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index c605e3e..315014c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -528,8 +528,9 @@
      * Hide IME using imeTargetWindow when requested.
      *
      * @param imeTargetWindowToken token of the (IME target) window on which IME should be hidden.
+     * @param displayId the id of the display the IME is on.
      */
-    public abstract void hideIme(IBinder imeTargetWindowToken);
+    public abstract void hideIme(IBinder imeTargetWindowToken, int displayId);
 
     /**
      * Tell window manager about a package that should not be running with high refresh rate
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0f793ed..481ad9e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -91,6 +91,7 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_BOOT;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_IME;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
@@ -7629,24 +7630,26 @@
         }
 
         @Override
-        public void hideIme(IBinder imeTargetWindowToken) {
+        public void hideIme(IBinder imeTargetWindowToken, int displayId) {
             synchronized (mGlobalLock) {
                 WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
-                if (imeTarget == null) {
-                    // The target window no longer exists.
-                    return;
+                ProtoLog.d(WM_DEBUG_IME, "hideIme target: %s ", imeTarget);
+                DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (imeTarget != null) {
+                    imeTarget = imeTarget.getImeControlTarget().getWindow();
+                    if (imeTarget != null) {
+                        dc = imeTarget.getDisplayContent();
+                    }
+                    // If there was a pending IME show(), reset it as IME has been
+                    // requested to be hidden.
+                    dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
                 }
-                imeTarget = imeTarget.getImeControlTarget().getWindow();
-                final DisplayContent dc = imeTarget != null
-                        ? imeTarget.getDisplayContent() : getDefaultDisplayContentLocked();
-                // If there was a pending IME show(), reset it as IME has been
-                // requested to be hidden.
-                dc.getInsetsStateController().getImeSourceProvider().abortShowImePostLayout();
-                if (dc.mInputMethodControlTarget == null) {
-                    return;
+                if (dc != null && dc.mInputMethodControlTarget != null) {
+                    ProtoLog.d(WM_DEBUG_IME, "hideIme Control target: %s ",
+                            dc.mInputMethodControlTarget);
+                    dc.mInputMethodControlTarget.hideInsets(
+                            WindowInsets.Type.ime(), true /* fromIme */);
                 }
-                dc.mInputMethodControlTarget.hideInsets(
-                        WindowInsets.Type.ime(), true /* fromIme */);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index e50bd25..bac9218 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -27,6 +27,7 @@
 import static android.graphics.GraphicsProtos.dumpPointProto;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.InsetsState.ITYPE_IME;
 import static android.view.SurfaceControl.Transaction;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
@@ -162,7 +163,6 @@
 import static com.android.server.wm.WindowStateProto.FORCE_SEAMLESS_ROTATION;
 import static com.android.server.wm.WindowStateProto.GIVEN_CONTENT_INSETS;
 import static com.android.server.wm.WindowStateProto.HAS_SURFACE;
-import static com.android.server.wm.WindowStateProto.IDENTIFIER;
 import static com.android.server.wm.WindowStateProto.IS_ON_SCREEN;
 import static com.android.server.wm.WindowStateProto.IS_READY_FOR_DISPLAY;
 import static com.android.server.wm.WindowStateProto.IS_VISIBLE;
@@ -762,6 +762,12 @@
             oldRotation = mPendingSeamlessRotate.getOldRotation();
         }
 
+        // Skip performing seamless rotation when the controlled insets is IME with visible state.
+        if (mControllableInsetProvider != null
+                && mControllableInsetProvider.getSource().getType() == ITYPE_IME) {
+            return;
+        }
+
         if (mForceSeamlesslyRotate || requested) {
             if (mControllableInsetProvider != null) {
                 mControllableInsetProvider.startSeamlessRotation();
@@ -2416,12 +2422,15 @@
         if (mAttrs.type == TYPE_APPLICATION_STARTING) {
             // Ignore mayUseInputMethod for starting window for now.
             // TODO(b/159911356): Remove this special casing (originally added in commit e75d872).
-        } else if (PixelFormat.formatHasAlpha(mAttrs.format)) {
-            // Support legacy use cases where transparent windows can still be ime target with
-            // FLAG_NOT_FOCUSABLE and ALT_FOCUSABLE_IM set.
-            // Certain apps listen for IME insets using transparent windows and ADJUST_NOTHING to
-            // manually synchronize app content to IME animation b/144619551.
-            // TODO(b/145812508): remove this once new focus management is complete b/141738570
+        } else {
+            // TODO(b/145812508): Clean this up in S, may depend on b/141738570
+            //  The current logic lets windows become the "ime target" even though they are
+            //  not-focusable and can thus never actually start input.
+            //  Ideally, this would reject windows where mayUseInputMethod() == false, but this
+            //  also impacts Z-ordering of and delivery of IME insets to child windows, which means
+            //  that simply disallowing non-focusable windows would break apps.
+            //  See b/159438771, b/144619551.
+
             final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
 
             // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are
@@ -2429,12 +2438,6 @@
             if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)) {
                 return false;
             }
-        } else if (!WindowManager.LayoutParams.mayUseInputMethod(mAttrs.flags)) {
-            // Can be an IME target only if:
-            // 1. FLAG_NOT_FOCUSABLE is not set
-            // 2. FLAG_ALT_FOCUSABLE_IM is not set
-            // 3. not a starting window.
-            return false;
         }
 
         if (DEBUG_INPUT_METHOD) {
@@ -3921,7 +3924,6 @@
 
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
-        writeIdentifierToProto(proto, IDENTIFIER);
         proto.write(DISPLAY_ID, getDisplayId());
         proto.write(STACK_ID, getRootTaskId());
         mAttrs.dumpDebug(proto, ATTRIBUTES);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 9340268..9d2393b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -188,10 +188,10 @@
     }
 
     @Test
-    public void testAfterDisplayChange_ModesAreUpdated() throws Exception {
-        SurfaceControl.DisplayConfig displayInfo = createFakeDisplayConfig(1920, 1080, 60f);
+    public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception {
+        SurfaceControl.DisplayConfig displayConfig = createFakeDisplayConfig(1920, 1080, 60f);
         SurfaceControl.DisplayConfig[] configs =
-                new SurfaceControl.DisplayConfig[]{displayInfo};
+                new SurfaceControl.DisplayConfig[]{displayConfig};
         FakeDisplay display = new FakeDisplay(PORT_A, configs, 0);
         setUpDisplay(display);
         updateAvailableDisplays();
@@ -205,20 +205,20 @@
                 0).getDisplayDeviceInfoLocked();
 
         assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
-        assertModeIsSupported(displayDeviceInfo.supportedModes, displayInfo);
+        assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
 
         Display.Mode defaultMode = getModeById(displayDeviceInfo, displayDeviceInfo.defaultModeId);
-        assertThat(defaultMode.matches(displayInfo.width, displayInfo.height,
-                displayInfo.refreshRate)).isTrue();
+        assertThat(defaultMode.matches(displayConfig.width, displayConfig.height,
+                displayConfig.refreshRate)).isTrue();
 
         Display.Mode activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
-        assertThat(activeMode.matches(displayInfo.width, displayInfo.height,
-                displayInfo.refreshRate)).isTrue();
+        assertThat(activeMode.matches(displayConfig.width, displayConfig.height,
+                displayConfig.refreshRate)).isTrue();
 
         // Change the display
         SurfaceControl.DisplayConfig addedDisplayInfo = createFakeDisplayConfig(3840, 2160,
                 60f);
-        configs = new SurfaceControl.DisplayConfig[]{displayInfo, addedDisplayInfo};
+        configs = new SurfaceControl.DisplayConfig[]{displayConfig, addedDisplayInfo};
         display.configs = configs;
         display.activeConfig = 1;
         setUpDisplay(display);
@@ -236,7 +236,7 @@
         displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
 
         assertThat(displayDeviceInfo.supportedModes.length).isEqualTo(configs.length);
-        assertModeIsSupported(displayDeviceInfo.supportedModes, displayInfo);
+        assertModeIsSupported(displayDeviceInfo.supportedModes, displayConfig);
         assertModeIsSupported(displayDeviceInfo.supportedModes, addedDisplayInfo);
 
         activeMode = getModeById(displayDeviceInfo, displayDeviceInfo.modeId);
@@ -248,6 +248,80 @@
                 addedDisplayInfo.refreshRate)).isTrue();
     }
 
+    @Test
+    public void testAfterDisplayChange_HdrCapabilitiesAreUpdated() throws Exception {
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0],
+                1000, 1000, 0);
+        display.hdrCapabilities = initialHdrCapabilities;
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        assertThat(mListener.changedDisplays).isEmpty();
+
+        DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(
+                0).getDisplayDeviceInfoLocked();
+
+        assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(initialHdrCapabilities);
+
+        // Change the display
+        Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities(
+                new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0);
+        display.hdrCapabilities = changedHdrCapabilities;
+        setUpDisplay(display);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+        DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+        displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+        displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+        assertThat(displayDeviceInfo.hdrCapabilities).isEqualTo(changedHdrCapabilities);
+    }
+
+    @Test
+    public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception {
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709};
+        display.colorModes = initialColorModes;
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        assertThat(mListener.changedDisplays).isEmpty();
+
+        DisplayDeviceInfo displayDeviceInfo = mListener.addedDisplays.get(0)
+                .getDisplayDeviceInfoLocked();
+
+        assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_BT709);
+        assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(initialColorModes);
+
+        // Change the display
+        final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT};
+        display.colorModes = changedColorModes;
+        setUpDisplay(display);
+        mAdapter.registerLocked();
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+        assertThat(mListener.changedDisplays.size()).isEqualTo(1);
+
+        DisplayDevice displayDevice = mListener.changedDisplays.get(0);
+        displayDevice.applyPendingDisplayDeviceInfoChangesLocked();
+        displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+
+        assertThat(displayDeviceInfo.colorMode).isEqualTo(Display.COLOR_MODE_DEFAULT);
+        assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes);
+    }
+
     private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
                                   float expectedXdpi,
                                   float expectedYDpi,
@@ -279,6 +353,9 @@
         public final SurfaceControl.DisplayInfo info;
         public SurfaceControl.DisplayConfig[] configs;
         public int activeConfig;
+        public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT };
+        public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0],
+                1000, 1000, 0);
 
         private FakeDisplay(int port) {
             this.address = createDisplayAddress(port);
@@ -306,8 +383,10 @@
                 () -> SurfaceControl.getDisplayConfigs(display.token));
         doReturn(display.activeConfig).when(() -> SurfaceControl.getActiveConfig(display.token));
         doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token));
-        doReturn(new int[] { 0 }).when(
+        doReturn(display.colorModes).when(
                 () -> SurfaceControl.getDisplayColorModes(display.token));
+        doReturn(display.hdrCapabilities).when(
+                () -> SurfaceControl.getHdrCapabilities(display.token));
         doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f))
                 .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token));
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
index 6bfe566..2026363 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/MockableLocationProviderTest.java
@@ -27,6 +27,7 @@
 
 import android.location.Criteria;
 import android.location.Location;
+import android.location.util.identity.CallerIdentity;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -68,8 +69,8 @@
                 true,
                 true,
                 Criteria.POWER_LOW,
-                Criteria.ACCURACY_FINE)
-        ));
+                Criteria.ACCURACY_FINE),
+                CallerIdentity.forTest(0, 1, "testpackage", "test")));
 
         mProvider = new MockableLocationProvider(lock, mListener);
         mProvider.setRealProvider(mRealProvider);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index 1bcfcbf..04a9cca 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -68,7 +68,7 @@
     @Mock private Context mContext;
     @Mock private AppOpsManager mAppOps;
 
-    private List<AppOpsManager.OnOpChangedInternalListener> mListeners = new ArrayList<>();
+    private List<AppOpsManager.OnOpChangedListener> mListeners = new ArrayList<>();
 
     private SystemAppOpsHelper mHelper;
 
@@ -82,15 +82,15 @@
                         eq(AppOpsManager.OP_COARSE_LOCATION),
                         isNull(),
                         eq(AppOpsManager.WATCH_FOREGROUND_CHANGES),
-                        any(AppOpsManager.OnOpChangedInternalListener.class));
+                        any(AppOpsManager.OnOpChangedListener.class));
 
         mHelper = new SystemAppOpsHelper(mContext);
         mHelper.onSystemReady();
     }
 
     private void sendAppOp(String packageName) {
-        for (AppOpsManager.OnOpChangedInternalListener listener : mListeners) {
-            listener.onOpChanged(AppOpsManager.OP_COARSE_LOCATION, packageName);
+        for (AppOpsManager.OnOpChangedListener listener : mListeners) {
+            listener.onOpChanged(AppOpsManager.OPSTR_COARSE_LOCATION, packageName);
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 4afed48..f0be9f1 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -64,6 +64,7 @@
 
 import com.android.internal.R;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.biometrics.sensors.LockoutTracker;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -825,6 +826,62 @@
     }
 
     @Test
+    public void testBiometricAuth_whenBiometricLockoutTimed_sendsErrorAndModality()
+            throws Exception {
+        testBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_TIMED,
+                BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT);
+    }
+
+    @Test
+    public void testBiometricAuth_whenBiometricLockoutPermanent_sendsErrorAndModality()
+            throws Exception {
+        testBiometricAuth_whenLockout(LockoutTracker.LOCKOUT_PERMANENT,
+                BiometricPrompt.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
+    }
+
+    private void testBiometricAuth_whenLockout(@LockoutTracker.LockoutMode int lockoutMode,
+            int biometricPromptError) throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+        when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+                .thenReturn(lockoutMode);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */, null /* authenticators */);
+        waitForIdle();
+
+        // Modality and error are sent
+        verify(mReceiver1).onError(eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+                eq(biometricPromptError), eq(0) /* vendorCode */);
+    }
+
+    @Test
+    public void testBiometricOrCredentialAuth_whenBiometricLockout_showsCredential()
+            throws Exception {
+        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+        when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+                .thenReturn(LockoutTracker.LOCKOUT_PERMANENT);
+        invokeAuthenticate(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */,
+                Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_STRONG);
+        waitForIdle();
+
+        verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
+        assertNotNull(mBiometricService.mCurrentAuthSession);
+        assertEquals(AuthSession.STATE_SHOWING_DEVICE_CREDENTIAL,
+                mBiometricService.mCurrentAuthSession.getState());
+        assertEquals(Authenticators.DEVICE_CREDENTIAL,
+                mBiometricService.mCurrentAuthSession.mPromptInfo.getAuthenticators());
+        verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+                eq(mBiometricService.mCurrentAuthSession.mPromptInfo),
+                any(IBiometricSysuiReceiver.class),
+                eq(0 /* biometricModality */),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                eq(TEST_PACKAGE_NAME),
+                anyLong() /* sessionId */);
+    }
+
+    @Test
     public void testCombineAuthenticatorBundles_withKeyDeviceCredential_andKeyAuthenticators() {
         final boolean allowDeviceCredential = false;
         final @Authenticators.Types int authenticators =
@@ -1199,6 +1256,29 @@
     }
 
     @Test
+    public void testCanAuthenticate_whenLockoutTimed() throws Exception {
+        testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_TIMED);
+    }
+
+    @Test
+    public void testCanAuthenticate_whenLockoutPermanent() throws Exception {
+        testCanAuthenticate_whenLockedOut(LockoutTracker.LOCKOUT_PERMANENT);
+    }
+
+    private void testCanAuthenticate_whenLockedOut(@LockoutTracker.LockoutMode int lockoutMode)
+            throws Exception {
+        // When only biometric is requested, and sensor is strong enough
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+
+        when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+                .thenReturn(lockoutMode);
+
+        // Lockout is not considered an error for BiometricManager#canAuthenticate
+        assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+                invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG));
+    }
+
+    @Test
     public void testAuthenticatorActualStrength() {
         // Tuple of OEM config, updatedStrength, and expectedStrength
         final int[][] testCases = {
@@ -1497,6 +1577,8 @@
             when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any()))
                     .thenReturn(enrolled);
             when(mFingerprintAuthenticator.isHardwareDetected(any())).thenReturn(true);
+            when(mFingerprintAuthenticator.getLockoutModeForUser(anyInt()))
+                    .thenReturn(LockoutTracker.LOCKOUT_NONE);
             mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FINGERPRINT, modality, strength,
                     mFingerprintAuthenticator);
         }
@@ -1504,6 +1586,8 @@
         if ((modality & BiometricAuthenticator.TYPE_FACE) != 0) {
             when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(enrolled);
             when(mFaceAuthenticator.isHardwareDetected(any())).thenReturn(true);
+            when(mFaceAuthenticator.getLockoutModeForUser(anyInt()))
+                    .thenReturn(LockoutTracker.LOCKOUT_NONE);
             mBiometricService.mImpl.registerAuthenticator(SENSOR_ID_FACE, modality, strength,
                     mFaceAuthenticator);
         }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index 8aa0406..b05a819 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -225,7 +225,11 @@
                 {BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
                         BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE},
                 {BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
-                        BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE}
+                        BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE},
+                {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT,
+                        BiometricManager.BIOMETRIC_SUCCESS},
+                {BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT,
+                        BiometricManager.BIOMETRIC_SUCCESS}
         };
 
         for (int i = 0; i < testCases.length; i++) {
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
index 8fc9d44..d4b299d 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
@@ -51,6 +51,11 @@
         }
 
         @Override
+        protected Object getDaemon() {
+            return null;
+        }
+
+        @Override
         protected BiometricUtils getBiometricUtils() {
             return null;
         }
@@ -61,7 +66,7 @@
         }
 
         @Override
-        protected void updateActiveGroup(int userId, String clientPackage) {
+        protected void updateActiveGroup(int userId) {
         }
 
         @Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
index 4bf0bb9..4ba08d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java
@@ -69,7 +69,7 @@
 
     private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null);
     private WindowManagerService mWms;
-    private DisplayArea.Root mRoot;
+    private RootDisplayArea mRoot;
     private DisplayArea<WindowContainer> mImeContainer;
     private DisplayContent mDisplayContent;
     private TaskDisplayArea mDefaultTaskDisplayArea;
@@ -91,27 +91,29 @@
     public void testBuilder() {
         final Feature foo;
         final Feature bar;
-
+        DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
+                                .upTo(TYPE_STATUS_BAR)
+                                .and(TYPE_NAVIGATION_BAR)
+                                .build())
+                        .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
+                                .all()
+                                .except(TYPE_STATUS_BAR)
+                                .build())
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
         DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
-                .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 0)
-                        .upTo(TYPE_STATUS_BAR)
-                        .and(TYPE_NAVIGATION_BAR)
-                        .build())
-                .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 1)
-                        .all()
-                        .except(TYPE_STATUS_BAR)
-                        .build())
-                .build(mWms, mDisplayContent, mRoot, mImeContainer, mTaskDisplayAreaList);
+                .setRootHierarchy(rootHierarchy)
+                .build(mWms);
 
-        policy.attachDisplayAreas();
-
-        assertThat(policy.getDisplayAreas(foo)).isNotEmpty();
-        assertThat(policy.getDisplayAreas(bar)).isNotEmpty();
+        assertThat(policy.getDisplayAreas(foo.getId())).isNotEmpty();
+        assertThat(policy.getDisplayAreas(bar.getId())).isNotEmpty();
 
         Matcher<WindowContainer> fooDescendantMatcher = descendantOfOneOf(
-                policy.getDisplayAreas(foo));
+                policy.getDisplayAreas(foo.getId()));
         Matcher<WindowContainer> barDescendantMatcher = descendantOfOneOf(
-                policy.getDisplayAreas(bar));
+                policy.getDisplayAreas(bar.getId()));
 
         // There is a DA of TYPE_STATUS_BAR below foo, but not below bar
         assertThat(fooDescendantMatcher.matches(
@@ -128,7 +130,7 @@
         assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue();
 
         List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot);
-        Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mRoot, mImeContainer,
+        Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer,
                 mDefaultTaskDisplayArea);
         actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
 
@@ -162,18 +164,21 @@
     public void testBuilder_createCustomizedDisplayAreaForFeature() {
         final Feature dimmable;
         final Feature other;
+        DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
+                new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
+                        .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0)
+                                .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+                                .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
+                                .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
+                                .build())
+                        .addFeature(other = new Feature.Builder(mPolicy, "Other", 1)
+                                .all()
+                                .build())
+                        .setImeContainer(mImeContainer)
+                        .setTaskDisplayAreas(mTaskDisplayAreaList);
         DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
-                .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 0)
-                        .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
-                        .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
-                        .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
-                        .build())
-                .addFeature(other = new Feature.Builder(mPolicy, "Other", 1)
-                        .all()
-                        .build())
-                .build(mWms, mDisplayContent, mRoot, mImeContainer, mTaskDisplayAreaList);
-
-        policy.attachDisplayAreas();
+                .setRootHierarchy(rootHierarchy)
+                .build(mWms);
 
         List<DisplayArea<? extends WindowContainer>> dimmableDAs =
                 policy.getDisplayAreas(dimmable.getId());
@@ -207,7 +212,7 @@
     }
 
     private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
-            DisplayAreaPolicyBuilder.Result policy, DisplayArea.Root root,
+            DisplayAreaPolicyBuilder.Result policy,
             DisplayArea<WindowContainer> ime,
             DisplayArea<ActivityStack> tasks) {
         Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>();
@@ -285,7 +290,7 @@
         }
     }
 
-    private static class SurfacelessDisplayAreaRoot extends DisplayArea.Root {
+    private static class SurfacelessDisplayAreaRoot extends RootDisplayArea {
 
         SurfacelessDisplayAreaRoot(WindowManagerService wms) {
             super(wms);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
index 6834ee5..351426a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -28,6 +28,10 @@
 import org.junit.Assert;
 import org.junit.Test;
 
+/**
+ * Build/Install/Run:
+ *  atest WmTests:DisplayAreaProviderTest
+ */
 @Presubmit
 public class DisplayAreaProviderTest {
 
@@ -77,7 +81,7 @@
 
         @Override
         public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
-                DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer) {
+                RootDisplayArea root, DisplayArea<? extends WindowContainer> imeContainer) {
             throw new RuntimeException("test stub");
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 1415c50..9d88ada 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -28,6 +28,8 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.clearInvocations;
 
 import android.graphics.Point;
@@ -158,4 +160,30 @@
         assertEquals(activity1, task1.isInTask(activity1));
         assertNull(task1.isInTask(activity2));
     }
+
+    @Test
+    public void testRemoveChildForOverlayTask() {
+        final Task task = createTaskStackOnDisplay(mDisplayContent);
+        final int taskId = task.mTaskId;
+        final ActivityRecord activity1 =
+                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity2 =
+                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        final ActivityRecord activity3 =
+                WindowTestUtils.createActivityRecordInTask(mDisplayContent, task);
+        activity1.setTaskOverlay(true);
+        activity2.setTaskOverlay(true);
+        activity3.setTaskOverlay(true);
+
+        assertEquals(3, task.getChildCount());
+        assertTrue(task.onlyHasTaskOverlayActivities(true));
+
+        task.removeChild(activity1);
+
+        verify(task.mStackSupervisor).removeTask(any(), anyBoolean(), anyBoolean(), anyString());
+        assertEquals(2, task.getChildCount());
+        task.forAllActivities((r) -> {
+            assertTrue(r.finishing);
+        });
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index f52905e..040717d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -166,8 +166,8 @@
         DisplayContent display = createNewDisplay();
         TaskDisplayArea secondTda =
                 new TaskDisplayArea(display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST);
-        display.mDisplayAreaPolicy.mRoot.addChild(secondTda, 1);
-        display.mDisplayAreaPolicy.mTaskDisplayAreas.add(secondTda);
+        display.addChild(secondTda, 1);
+        display.mTaskDisplayAreas.add(secondTda);
         // Current focused window
         ActivityStack focusedStack = createTaskStackOnDisplay(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, display);
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 4a0f48cf..360d73b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -67,7 +67,6 @@
 
 import android.graphics.Insets;
 import android.graphics.Matrix;
-import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
@@ -229,22 +228,11 @@
         appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
         imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
 
-        // Visible app window with flags FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM can't be IME
-        // target while an IME window can never be an IME target regardless of its visibility
-        // or flags.
-        assertFalse(appWindow.canBeImeTarget());
+        // Visible app window with flags can be IME target while an IME window can never be an IME
+        // target regardless of its visibility or flags.
+        assertTrue(appWindow.canBeImeTarget());
         assertFalse(imeWindow.canBeImeTarget());
 
-        // b/145812508: special legacy use-case for transparent/translucent windows.
-        appWindow.mAttrs.format = PixelFormat.TRANSPARENT;
-        assertTrue(appWindow.canBeImeTarget());
-
-        appWindow.mAttrs.format = PixelFormat.OPAQUE;
-        appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
-        assertFalse(appWindow.canBeImeTarget());
-        appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
-        assertTrue(appWindow.canBeImeTarget());
-
         // Verify PINNED windows can't be IME target.
         int initialMode = appWindow.mActivityRecord.getWindowingMode();
         appWindow.mActivityRecord.setWindowingMode(WINDOWING_MODE_PINNED);
diff --git a/tests/DynamicCodeLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
index 62c1ba8..2d58ce8 100644
--- a/tests/DynamicCodeLoggerIntegrationTests/Android.mk
+++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk
@@ -36,8 +36,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary
 LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp
-LOCAL_C_INCLUDES += \
-    $(JNI_H_INCLUDE)
+LOCAL_HEADER_LIBRARIES := jni_headers
 LOCAL_SDK_VERSION := 28
 LOCAL_NDK_STL_VARIANT := c++_static
 
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index e3c7cfa..b7d72db 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -27,6 +27,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -58,6 +59,9 @@
 
     private WatchdogEventLogger mLogger = new WatchdogEventLogger();
 
+    @Rule
+    public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+
     @Before
     public void setUp() throws Exception {
         runPhase("cleanUp");
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 8104a3f..55bbe22 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -34,6 +34,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -87,6 +88,9 @@
 
     private WatchdogEventLogger mLogger = new WatchdogEventLogger();
 
+    @Rule
+    public AbandonSessionsRule mHostTestRule = new AbandonSessionsRule(this);
+
     @Before
     public void setUp() throws Exception {
         deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/AbandonSessionsRule.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/AbandonSessionsRule.java
new file mode 100644
index 0000000..b086213
--- /dev/null
+++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/AbandonSessionsRule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.rollback.host;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.rules.ExternalResource;
+
+public class AbandonSessionsRule extends ExternalResource {
+    private final BaseHostJUnit4Test mHost;
+
+    public AbandonSessionsRule(BaseHostJUnit4Test host) {
+        mHost = host;
+    }
+
+    @Override
+    protected void before() throws Throwable {
+        abandonSessions(mHost.getDevice());
+    }
+
+    @Override
+    protected void after() {
+        try {
+            abandonSessions(mHost.getDevice());
+        } catch (Exception ignore) {
+        }
+    }
+
+    /**
+     * Abandons all sessions to prevent interference in our tests.
+     */
+    private static void abandonSessions(ITestDevice device) throws Exception {
+        // No point in abandoning applied or failed sessions. We care about ready sessions only.
+        String cmdListReadySessions =
+                "pm list staged-sessions --only-sessionid --only-parent --only-ready";
+        String output = device.executeShellCommand(cmdListReadySessions);
+        if (output.trim().isEmpty()) {
+            // No sessions to abandon
+            return;
+        }
+        // Ensure we have sufficient privilege to abandon sessions from other apps
+        device.enableAdbRoot();
+        device.executeShellCommand("for i in $(" + cmdListReadySessions
+                + "); do pm install-abandon $i; done");
+        device.disableAdbRoot();
+    }
+}
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 1d363570..02f52865 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -19,8 +19,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.net.quirks.IPv6ProvisioningLossQuirk;
-import android.net.quirks.IPv6ProvisioningLossQuirkParcelable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -48,7 +46,7 @@
         builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
         // lease will expire in two hours
         builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000);
-        // cluster stays null this time around
+        // groupHint stays null this time around
         builder.setDnsAddresses(Collections.emptyList());
         builder.setMtu(18);
         in = builder.build();
@@ -71,7 +69,7 @@
         // Verify that this test does not miss any new field added later.
         // If any field is added to NetworkAttributes it must be tested here for parceling
         // roundtrip.
-        assertEquals(6, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+        assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
                 .filter(f -> !Modifier.isStatic(f.getModifiers())).count());
     }
 
@@ -106,22 +104,6 @@
         assertEquals(in.confidence, out.confidence, 0.01f /* delta */);
     }
 
-    @Test
-    public void testIPv6ProvisioningLossQuirkParceling() throws Exception {
-        final NetworkAttributes.Builder builder = new NetworkAttributes.Builder();
-        final IPv6ProvisioningLossQuirkParcelable parcelable =
-                new IPv6ProvisioningLossQuirkParcelable();
-        final long expiry = System.currentTimeMillis() + 7_200_000;
-
-        parcelable.detectionCount = 3;
-        parcelable.quirkExpiry = expiry; // quirk info will expire in two hours
-        builder.setIpv6ProvLossQuirk(IPv6ProvisioningLossQuirk.fromStableParcelable(parcelable));
-        final NetworkAttributes in = builder.build();
-
-        final NetworkAttributes out = new NetworkAttributes(parcelingRoundTrip(in.toParcelable()));
-        assertEquals(out.ipv6ProvLossQuirk, in.ipv6ProvLossQuirk);
-    }
-
     private <T extends Parcelable> T parcelingRoundTrip(final T in) throws Exception {
         final Parcel p = Parcel.obtain();
         in.writeToParcel(p, /* flags */ 0);
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index cdf4b3f..fb84611 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertEquals;
 
 import android.net.ipmemorystore.NetworkAttributes;
-import android.net.quirks.IPv6ProvisioningLossQuirk;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -53,8 +52,6 @@
         }
         assertEquals(sum, NetworkAttributes.TOTAL_WEIGHT, EPSILON);
 
-        final IPv6ProvisioningLossQuirk ipv6ProvLossQuirk =
-                new IPv6ProvisioningLossQuirk(3, System.currentTimeMillis() + 7_200_000);
         // Use directly the constructor with all attributes, and make sure that when compared
         // to itself the score is a clean 1.0f.
         final NetworkAttributes na =
@@ -64,7 +61,7 @@
                         "some hint",
                         Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}),
                                 Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})),
-                        98, ipv6ProvLossQuirk);
+                        98);
         assertEquals(1.0f, na.getNetworkGroupSamenessConfidence(na), EPSILON);
     }
 }